当前位置:网站首页>The Handler you really understand?

The Handler you really understand?

2022-08-02 14:51:00 A leaf can't cover the sky

目录

一、Handler、Looper、MessageQueue、Message的关系

1.1、消息机制流程

1.2、Looper创建

二、News team resolved

2.1、sendMessage

2.2、enqueueMessage

2.3、postSyncBarrier同步屏障消息

三、Message distribution analytic

3.1、Looper.loop()

3.2、Handler.dispatchMessage

3.3、ThreadLocal(Can be extended content to understand)

四、Handler高频面试考点


先说两句废话,Before this for a long time,由于各种原因吧,Really long time no wrote,Human nature of the inert really terrible,We should try to overcome inertia,好在今天,我又回来了,Pick up learning summary,养成一个良好的学习习惯,Rui hired!

Today we are going to review summarizeHandler的相关知识点,Sort out the problem of some of the common,Can turn out when next time we need to have a look,The document is a reference for class network《移动端架构师》课程中的Handler部分的讲解.

一、Handler、Looper、MessageQueue、Message的关系

1.1、消息机制流程

首先来看下面这张图:A thread of up to aLooper;一个Looper有一个MessageQueue;一个MessageQueue对应多个message;一个MessageQueue对应多个Handler.Through this chart we to the wholeAndroidMessaging process together to understand:

AndroidMessage mechanism is basically by theHandler、Looper、MessageQueue、MessageThis a few classes to support,实际上整个AndroidSystem is a system message driven,If there is no this a few class,Android中的各种事件、All kinds of messages cannot be enforced,So it is very important to understand the message mechanism is.

Handler:Responsible for sending messages and handle the message,No matter the way we use which sends the message,最终都是通过enqueueMessage将消息加入到MessageQueueThe message queue to.

MessageQueue:Because we sent messages is not immediately get perform,So the message need a place to store it,因此就有了MessageQueue这个消息队列.Its interior is implemented by a singly linked list,The same time will only be triggered by a message,So the message in the queue will in itself be triggered time order in the queue,无论何时,This message queue head it triggered time are the most close to.

A queue of a total of three types of news:BarrierMessage:同步屏障消息;AsyncMessage:异步消息;Message:同步消息.

The message in the queue to allow them to perform in the necessary time?此时就需要依靠LooperThe news of the infinite loop drive.

Looper:The news of the infinite loop drive,It is responsible for the associated thread and message distribution,Looper创建的时候会创建一个MessageQueue,Looper会通过loop()Methods open an infinite loop,It will start from scratch traversal queue,不断调用MessageQueue的next()Methods to check whether there is a meet the conditions of news,Have, take out the message will be distributed and perform,No obstruction innext()方法中.当Looper的quit()方法被调用时会调用MessageQueue的quit(),此时next()会返回null,之后loop()Methods will follow.

Message:消息,Within each message are the following several important properties of:

when:This message is executed the timestamp of the,This is it the only on the basis of sorting in the queue;

next:Because the message queue is a singly linked list,So within each message will contain a message under the reference relationship;

target:On behalf of each message is whichHandler对象发送的,Also by thisHandlerObject to spend.

The whole process of the message loop summarized below:

Handler通过sendMessage()方法发送消息Message到消息队列MessageQueue中,Looper通过loop()Methods get the trigger conditionMessage,然后将Message交给对应的Handler,最后Handler调用自身的handleMessage()方法处理Message.

1.2、Looper创建

We usually use in the main threadHandler时,Did not write aboutLooper的相关代码,Why can also send message?

  • 在ActivityThread.main()方法中调用了Looper.prepareMainLooper()方法创建了主线程的Looper,然后调用了loop()Open the message loop
  • 通常我们说ActivityThread就是主线程,But in fact it is not a thread,而是主线程操作的管理者
class Handler{
    public Handler() {this(null, false);}
    public Handler(boolean async) {this(null, async);}
    public Handler(Callback callback, boolean async) {
        // 如果没调用Looper.prepare则为空
        // 主线程创建Handler不用创建Looper,就是因为ActivityThreadIn the process of entry callsLooper.prepareMainLooper()帮我们创建好了
        mLooper = Looper.myLooper();
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
}

Looper.myLooper()Is is how to ensure access to this threadlooper对象的呢?

class Looper{
    // sThreadLocal是static变量,Can understand simple it is equivalent toMap,key是线程,value是Looper
    // You can use the current threadsThreadLocal获取当前线程所属的Looper
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    // LooperThe thread's message queue
    final MessageQueue mQueue;
    
    private static void prepare(boolean quitAllowed) {
        // 一个线程只能有一个Looper,prepare不能重复调用
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
    
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
    }
    
    public static @Nullable Looper myLooper() {
        // 具体看ThreadLocal类源码的get方法
        // 简单理解相当于map.get(Thread.currentThread())获取当前线程的Looper
        return sThreadLocal.get();
    }
}

二、News team resolved

2.1、sendMessage

According to our usual experience,We can know when to send a message to,一般有两种方式:postXXX和sendXXX,No matter we use what kind of way,最后都会以MessageIn the form of inserted into the message queue,比如你使用post发送消息时,都会通过getPostMessage()将Runnable对象包装成Message对象,然后再次调用sendMethod to add it to the queue:

class Handler{
    public final boolean post(Runnable r){
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    // 将Runnable包装成Message
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
}

class Message{
    public static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;
    private static final int MAX_POOL_SIZE = 50;

    // 消息复用
    public static Message obtain() {
        // Singly linked list in the form of object pool
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

    // 消息回收
    void recycleUnchecked() {
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }
}

This is used when get messagesMessage.obtain()方法,这是MessageThe ability to provide us with the news of the reuse pool,Inside it will cache50个Message对象,Use this approach can be directly from the reuse pool message,不用new对象,Will not produce a lot of temp,因此不会造成CPUThe problem of high memory usage rate.Since the message pool's ability to provide a Shared object,So will certainly provide object recovery ability,When each message is carried out after completion of,都会走到recycleUnchecked()方法中,从而将Message对象重置,数据被清空,Then insert it into the head of the linked list node,Thus formed a team head reuse message pool.

2.2、enqueueMessage

MessageQueue是一个单项链表,So it's each node contains anextObject is used to point to the next node:

No matter use which kinds of sending the message the way,最终都会走到enqueueMessage方法里面:

  • If the new message is triggered timewhenThan any a message in the queue early,So is inserted into the team head,唤醒Looper
  • If the current message queue head is synchronous barrier,New messages are asynchronous message,So also want to wake upLooper

To see this part of the code below,Through the comments to do that:

class Handler{
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        // 把msg的target赋值成当前的Handler
        msg.target = this;
        ...
        return queue.enqueueMessage(msg, uptimeMillis);
    }
}

class MessageQueue{
    boolean enqueueMessage(Message msg, long when) {
        // 普通消息的target字段不能为空,Or don't know whom to handle this message
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }

        synchronized (this) {
            msg.when = when;
            // mMessageIs always a team head message
            Message p = mMessages;
            boolean needWake;
            // If the team head message is empty,或消息的when=0不需要延迟,或newMsg.when<headMsg.when,则插入队头
            if (p == null || when == 0 || when < p.when) {
                // 插入队头,把newMsg.nextPoint to the team head nodep,newMsg赋值给mMessage,newMsgWas the team head
                msg.next = p;
                mMessages = msg;
                // 如果当前Looper处于休眠状态,Is the need to wake up after insert message
                needWake = mBlocked;
            } else {
                // Do you want to wake upLooper=The dormant&Team head messages are synchronous barrier&New messages are asynchronous message
                // In order to make an asynchronous message executed as early as possible
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    // Find the message place in chronological order
                    // msg.when=2-->msg.when=4-->msg.when=6
                    // msg.when=2-->msg.when=4-->【newMsg.when=5】-->msg.when=6
                    // prevThe last object is the queue timestamp is less than the new message timestampmessage对象
                    prev = p; 
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                // We must adjust the relationship between a reference list node
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // 唤醒Looper,走的是native方法
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
}

2.3、postSyncBarrier同步屏障消息

  • message.target=null,This kind of news is not true to perform,它起到flag标记的作用,MessageQueueAt the time of the message queue traversal,If the team head is synchronous barrier message,So will ignore the synchronous message,Priority for implementation of asynchronous message.这就是它的目的,In general asynchronous messages and synchronization barrier will be used together.
  • 异步消息&同步屏障使用场景
  1. ViewRootImplVertical sync signal receiving screen event is used to driveUI测绘
  2. ActivityThread接收AMSThe event driven life cycle
  3. InputMethodManagerDistribution of soft keyboard input events
  4. PhoneWindowManagerDistribution of phone page events
class MessageQueue{
    public int postSyncBarrier() {
        // currentTimeMillis()系统当前时间,即日期时间,The system can modify Settings,如果设置系统时间,Change the time value will be
        // uptimeMillis()Since after the boot time,Do not include deep sleep time
        // sendMessageDelay,postDelayAlso use the timestamp
        // Mean to send this message,During this period if the device into a dormant state,Then the message will not be performed,After wake up will be device to perform
        return postSyncBarrier(SystemClock.uptimeMillis());
    }

    private int postSyncBarrier(long when) {
        synchronized (this) {
            // 从消息池复用,Build the new message body
            final Message msg = Message.obtain();
            // 并没有给target字段赋值,To distinguish whether the synchronization barrier,就看target是否等于null
            msg.when = when;

            Message prev = null;
            Message p = mMessages;
            // Iterates through all message queue,直至找到一个message.when>msg.when的消息,The location of the decided to insert a new news
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            // If find the right position is inserted into the
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
                // 如果没有找到,Directly on the team head
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }
}

 注意:Barrier message when inserted into the queue,Not to take the initiative to awaken the thread,Because of the barrier messages don't need to be performed.When news is so barrier is removed from the queue?答案是:Who add who will remove,比如ViewRootImplAt the time of receiving vertical synchronous signal sends an asynchronous message and a synchronous barrier message,When is in charge of the implementation of asynchronous message,ViewRootImplWill the synchronous barriers removed from the queue.

三、Message distribution analytic

3.1、Looper.loop()

The queue messages are able to get out,According to the analysis of the flow chart,它是靠Looper类调用loop()Methods open an infiniteforCirculation message drive,如下代码所示:

class Looper{
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
        ......
        for (;;) {
            // 无限循环,调用MessageQueue的nextAccess to the executable message,Note that the official comments,这里可能会阻塞
            Message msg = queue.next(); // might block
            if (msg == null) {
                return;
            }
            ......
            // 分发消息
            msg.target.dispatchMessage(msg);
            ......
            // 回收message对象,Keep the reuse
            msg.recycleUnchecked();
        }
    }
}

在loop()方法的内部,会调用MessageQueue的next()Method to get an executable message object.

注意:在Message msg = queue.next();This line of code behind the official gave a comment:might block,它的意思是,该next()Methods into a state may make the current thread blocking,This method will not have a return value at this time,The subsequent code won't be executed,So the infinite hereforCirculation is not always empty polling like this,Its purpose is to keep this thread to exit.

那么接下来我们就来看一下MessageQueue的next()方法:

class MessageQueue{
    Message next() {
        int nextPollTimeoutMillis = 0;
        for (;;) {
            // 如果nextPollTimeoutMillis>0,则Looper会进入休眠状态,The first value is equal to the0,所以不会
            // If the first cycle was not found to deal with the news of the,则nextPollTimeoutMillis会被更新
            // 在第二次循环时,如果nextPollTimeoutMillis!=0,Looper线程会进入阻塞状态
            // During this period the main thread has no real work to do,会释放CPU资源,This method will timeout autonomous recovery,Passive recovery or insert a new news
            nativePollOnce(ptr, nextPollTimeoutMillis);

            // 当Looper被唤醒时,会继续向下执行
            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                // If the team head's barrier,Are trying to find an asynchronous message
                if (msg != null && msg.target == null) {
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // If above the time that the news of the find is not to need to perform
                        // 则更新nextPollTimeoutMillis,即:The next cycle need blocking time value
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Found the message of the need to deal with
                        mBlocked = false;
                        // As a result of this message is about to be,So we need to remove it from the queue
                        // By adjusting the node points to the relationship between,To achieve the purpose of queue element removed
                        if (prevMsg != null) {
                            // To delete messages in the queue in the middle of the position
                            prevMsg.next = msg.next;
                        } else {
                            // Want to delete the messages in the team head position
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // 如果没有消息,即队列为空,LooperWill enter a state of infinite block,Until a new message arrives at
                    nextPollTimeoutMillis = -1;
                }

                // When the queue messages for empty,Is all the tasks are done,Or team head message has reached the execution time point
                // Distributed notice at this time LooperIs about to enter idle
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0)return
            }
            // 注册MessageQueue.IdleHandler,Can monitor the current threadLooper是否处于空闲状态,Also means that if thread is idle
            // In the main thread can listen for this event to do lazy initialization,数据加载,Optimize operation such as log report
            // Is not a task to submit,Avoid taking important resources
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                boolean keep = false;
                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }
            // 此时置为0,Since listening to the threads of free,那么在queueIdle回调里,May will produce a new message again
            // In order to make implementation of news as early as possible,So I don't need to sleep at this time
            nextPollTimeoutMillis = 0;
        }
    }
}

3.2、Handler.dispatchMessage

Here we need to know is the priority message distribution:

1. Message的回调方法:message.callback.run(),优先级最高
2. Handler的回调方法:Handler.mCallback.handleMessage(msg)
3. Handler的默认方法:Handler.handleMessage(msg) 

class Handler{
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
}

For the use of these three ways,We simply illustrated as follows:

        // 1.直接在Runnable中处理任务
        Handler handler1 = new Handler();
        // When the news in the consumer is the callback toMessage的Callback,这个callbackIs passed in hereRunnable对象
        handler1.post(new Runnable() {
            @Override
            public void run() {

            }
        });
        // 2.使用Handler.CallbackTo receive the message processing
        Handler handler2 = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(@NonNull Message message) {
                return true;
            }
        });
        // 3.使用handleMessageTo receive the message processing
        Handler handler = new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
            }
        };

3.3、ThreadLocal(Can be extended content to understand)

ThreadLocal提供了线程独有的局部变量存储能力,可以在整个线程存活的过程中随时取用.

Thread--->ThreadLocalMap--->Entry--->key:ThreadLocal & value:T

ThreadLocalMap的数据模型:

  • 每个线程对应一个ThreadLocalMap对象,Nature is a hash array implementationmap
  • 每个元素都是Entry对象,key=threadLocal,value为任意值
class ThreadLocal{
    public void set(T value) {
        // 获取当前线程的ThreadLocalMap对象
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value); // 更新值,key=threadLocal
        else
            createMap(t, value); // 创建并设置值
    }

    void createMap(Thread t, T firstValue) {
        // 每个线程都有一个ThreadLocalMap,key为threadLocal对象
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

    public T get() {
        // 获取当前线程对象,通过线程获取ThreadLocalMap对象
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        // 不为空则取值
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

    private T setInitialValue() {
        // Get the calledthreadLocal的初始值
        T value = initialValue();
        // 得到当前线程
        Thread t = Thread.currentThread();
        // To obtain or create each threadThreadLocalMap,And the initial values stored in
        // So as to realize variable copy,Become a thread exclusive
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

    static class ThreadLocalMap{
        // 本质是一个数组
        private Entry[] table;
        // 继承自WeakReference
        static class Entry extends WeakReference<ThreadLocal<?>> {
            Object value;
            Entry(ThreadLocal<?> k, Object v) {
                // keyIf be refer to hold,As if the object held by reference to the strong reference is empty,在GCScan will then be recycled
                // Thread pool must be noticed when using after completion of task execution,需手动把Value移除掉,Otherwise easy to cause a memory leak
                super(k);
                // value Strong reference value assignment
                value = v;
            }
        }
    }
}

四、Handler高频面试考点

1、为什么主线程不会因为Looper.loop()里的死循环卡死?

主线程确实是通过Looper.loop()进入了循环状态,Because the main Cheng Cai won't like usually create common thread,当可执行代码执行完后,线程生命周期就终止了.

在主线程的MessageQueue没有消息时,便阻塞在MessageQueue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,Until a new message arrives at,So the main thread is dormant in most of the time,并不会消耗大量CPU资源.

这里采用的Linux的epoll机制,是一种IO多路复用机制,可以同时监控多个文件描述符,当某个文件描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作拿到最新的消息,进而唤醒等待的线程.

2、post和sendMessageThis what is the difference between two ways of sending messages?

post一类的方法发送的是Runnable对象,But in the final will be encapsulated intoMessage对象,将Runnable对象赋值给Message对象中的callback变量,然后交由sendMessageAtTime()方法发送出去.在处理消息时,会在dispatchMessage()方法里首先被handleCallback(msg)方法执行,实际上就是执行Message对象里面的Runnable对象的run()方法.

sendMessage一类的方法发送的直接是Message对象,处理消息时,在dispatchMessage里优先级会低于handleCallback(msg)方法,是通过自己重写的handleMessage(msg)方法执行.

3、为什么要通过Message.obtain()方法获取Message对象?

Message.obtain()方法可以从全局消息池中得到一个空的Message对象,这样可以有效节省系统资源.同时,通过各种obtain重载方法还可以得到一些Message的拷贝,或对Message对象进行一些初始化.

4、Handler实现发送延迟消息的原理是什么?

我们平时用postDelayed()与sendMessageDelayed()来发送延迟消息,其实最终都是将延迟时间转为确定时间,然后通过sendMessageAtTime()--->enqueueMessage--->queue.enqueueMessage这一系列方法将消息插入到MessageQueue中.所以并不是先延迟再发送消息,而是直接发送消息,再借助MessageQueue的设计来实现消息的延迟处理.

消息延迟处理的原理涉及MessageQueue的两个静态方法MessageQueue.next()和MessageQueue.enqueueMessage().通过Native方法阻塞线程一定时间,As news of the execution time to remove again after messages.

5、同步屏障SyncBarrier是什么?有什么作用?

在一般情况下,同步和异步消息处理起来没有什么不同,只有在设置了同步屏障后才会有差异.同步屏障从代码层面上看是一个Message对象,但是其target属性为null,用以区分普通消息.在MessageQueue.next()Methods if the current message is a synchronous barrier,则跳过后面所有的同步消息,找到第一个异步消息来处理.

6、IdleHandler是什么?有什么作用?

当消息队列没有消息时调用或者如果队列中仍有待处理的消息,但都未到执行时间时,也会调用此方法,Used to monitor the main thread of the idle state.

7、Why not staticHandler会导致内存泄漏?如何解决?

非静态的内部类、匿名内部类、局部内部类都Implicitly hold其外部类的引用,举个栗子:在Activity中创建的HandlerImplicitly holdActivity的引用.

当我们在主线程使用Handler的时候,Handler会默认绑定这个线程的Looper对象,并关联其MessageQueue,Handler发出的所有消息都会加入到这个MessageQueue中.Looper对象的生命周期贯穿整个主线程的生命周期,所以当Looper对象中的MessageQueueThere is also not processedMessage时,因为每个Message都持有Handler的引用,所以Handler无法被回收,It holds a reference external classActivity也就无法被回收,造成内存泄漏.

解决方案:使用静态内部类+弱引用的方式

class MainActivity{
       private static class MyHandler extends Handler {
        //弱引用,在垃圾回收时,activity可被回收
        private WeakReference<MainActivity> mWeakReference;

        public MyHandler(MainActivity activity) {
            this.mWeakReference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //清空handler管道和队列
        mHandler.removeCallbacksAndMessages(null);
    }
}

8、How to pop up in the child threadToast?

调用Looper.prepare()以及Looper.loop(),But bear in mind that after task execution thread,需要手动调用Looper.quitSafely(),否则线程不会结束.

OK,Today's content is about to end here,谢谢大家,下期再会!

原网站

版权声明
本文为[A leaf can't cover the sky]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/214/202208021402191419.html