4-Handler message mechanism source code analysis

    Handler is an asynchronous callback mechanism provided by Android. It mainly realizes inter-thread communication of messages. Then the question arises. Why do we need Handler? There are mainly two reasons. One is to avoid time-consuming operations blocking the main thread (including network requests). UI stuck or ANR, the Android system requires the user to put these operations in the sub-thread, 2 Android also requires that the UI cannot be updated in the sub-thread (estimated to be based on thread safety issues), this requires a mechanism, in the sub-thread The message is passed to the main thread in the thread, so there is a Handler.

    To implement asynchronous callback, it is not enough to have Handler alone. Android also provides Looper, Message, and MessageQueue. These classes together realize the asynchronous callback function. Usually, we instantiate a Handler object mHandler directly in the Activity, send a message through mHandler in the child thread, and then receive the processing method in the callback method passed in when instantiating the Handler. At this time, the message has reached the main thread. Therefore, we start from instantiating Handler for in-depth analysis.

1, start instantiating Handler

1,在主线程实例化Handler
private Handler mHandler=new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            return false;
        }
    });

2,进入Handler内部实现,到了最终的构造方法中

   public Handler(@Nullable Callback callback, boolean async) {
        ...
        mLooper = Looper.myLooper();//取出保存好的Looper
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;//取出Looper中的消息队列
        mCallback = callback;   //后面处理消息的回调
        mAsynchronous = async;
    }

3,取出保存好的Looper的过程
   public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

4,ThreadLocal是什么,起的作用是什么?具体的看下ThreadLocal中set/get()的实现
ThreadLocal.java

  ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

    public void set(T value) {
        Thread t = Thread.currentThread();//当前线程
        ThreadLocalMap map = getMap(t);//得到线程中的一个变量,作用类似于HashMap,可以通过k-v保存数据
        if (map != null)
            map.set(this, value);       //map以ThreadLocal为key,以Looper为value
        else
            createMap(t, value);        //创建一个以ThreadLocal为key,以Looper为value的map给thread
    }

   public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);//取出map中的value,也就是looper
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

所以,Looper中的sThreadLocal明着像是一个保存looper的容器,但实际却是自己为key,looper为value,保存在线程Thread中的map中

2. After the Handler is instantiated, we see that the Looper and MessageQueue are taken out internally, so it is necessary to know how these are generated

1,在主线程中取出来的,那肯定是在主线程实例的,而且是在主线程(ActivityThread)刚启动的时候

ActivityThread.java
   public static void main(String[] args) {
        ...
        Looper.prepareMainLooper();//重点1
        ...
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);//这里之前详细分析过了,主要为了application/activity的创建和启动
        ...
        Looper.loop();//重点2

        throw new RuntimeException("Main thread loop unexpectedly exited");//抛异常, 主线程的loop出乎意料的退出了
    }

2,重点1,Looper是如何产生的?
    Looper.java
   public static void prepareMainLooper() {
        prepare(false);//关键语句,实例一个Looper,并保存
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();//实例化的Looper赋给sMainLooper
        }
    }   

  private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));//通过threadLocad保存new出来的Looper
    }

 public static @Nullable Looper myLooper() {
        return sThreadLocal.get();//取出来
    }

3,重点2 Looper是如何工作的?
     public static void loop() {
        final Looper me = myLooper();
        ...        
        final MessageQueue queue = me.mQueue;
        ...
        for (;;) {
            Message msg = queue.next(); // might block 官方注解:可能阻塞,后续分析
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            msg.target.dispatchMessage(msg);
        ...
        }
    }


简化以后就很明了了,loop()中取出之前实例好的looper,并取出looper中的消息队列queue,开启死循环,不停的执行消息队列取消息操作,
message.target是私有变量handler

Handler.java

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {//一般常见于使用handler.postXX(Runnable)发送的消息
            handleCallback(msg);
        } else {
            if (mCallback != null) {这是之前实例化Handler传进去的回调监听类
                if (mCallback.handleMessage(msg)) {//回调类的处理方法,实际在activity上,这样就把消息队列的消息传到主线程上了
                    return;
                }
            }
            handleMessage(msg);//如果实例化Handler未传入回调类,则必须重新Handler本身的HandleMessage()方法,就是这个方法,同样的,把消息传到主线程上了
        }
    }

小结:通过查看Looper的实例化和Looper.loop()的调用,我们清楚的看到了消息的处理流程,
loop()依次从messageQueue中取出message,然后从message中取出handler去分配处理消息。

3. Now that the message message is taken out from the message queue, there must be a process of putting the message into the message queue

1,通过handler发送message的方法有2类,一种是send->Message系列,一种是post->Runnable系统

方法中带Message参数    
sendMessage()//常用
sendMessageDelayed()//延迟接收的消息
sendMessageAtTime()//准确时间接收的消息
sendMessageAtFrontQueue()//见名知义,放到消息队列最前面,也就是马上执行的消息
sendEmptyMessage()//空消息
sendEmptyMessageAtTime()
sendEmptyMessageDelayed()

方法中带Runnable参数,通过getPostMessage()生成Message,runnable参数传入到message中(处理消息时使用此回调)
post()
postDelayed()
postAtTime()
postAtFrontOfQueue()

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

上面所有发送消息的方法,最终都是通过sendMessageAtTime(Message msg, long uptimeMillis)来发送的(方法千千万,最终汇总),uptimeMillis为处理消息的时间    


 public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;//这是Looper中的消息队列
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

   private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;//this就是当前的handler,处理消息时就是用的msg中的target
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);//最终,handler把消息送到了消息队列中,然后消息队列进行入队操作
    }

4. Beat the drums and pass the flowers, the message will finally be sent to the message queue

0,在消息message进入消息队列messagequeue之前,我们需要对Message和MessageQueue的基本结构有初步的了解,方便了解入队的整个过程
Message:有个when字段,表示当前消息处理的时间,同样的,message关联指向了另一个message,字段名为next,所以,整个Message可以组成一个链表结构
MessageQueue:消息队列管理保存着消息,因为Message可以组成一个链表结构,所以MessageQueue只需要持有一个Message对象就能完成对Message的保存和管理,
因为消息队列有处理时间的要求,所以消息队列持有的头消息必定是最早处理的(when最小),所以在入队的过程就必然需要有对时间的比较。

1,enqueue的就是入队的意思
boolean enqueueMessage(Message msg, long when) {
        ...

        synchronized (this) {
           ...

            msg.markInUse();
            msg.when = when;//将消息处理的时间放到消息的when字段中
            Message p = mMessages;//队列持有的头消息
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {//如果没有头消息 或 当前消息要立即处理 或当前消息处理时间早于头消息时间
                // New head, wake up the event queue if blocked.
                msg.next = p;                            //头消息变成msg的next消息,这样就当前消息变成了头消息,原先的头消息变成第二个消息对象
                mMessages = msg;                        //msg赋值给消息对象持有的头消息
                needWake = mBlocked;                    //不需要唤醒,这个变量后面再讨论
            } else {                                    //下面的操作是msg要插入到消息链表中间去

                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;//临时变量
                for (;;) {//死循环遍历消息链表,查找msg需要插入的位置
                    prev = p;//当前查找的消息放到prev上,第一次当然是消息队列持有的头消息
                    p = p.next;//取出下一个消息为p   此时 prev->p
                    if (p == null || when < p.when) {//如果下一个消息为空(到链表尾巴上了) 或者时间戳小于下一个消息的时间戳 则位置找到了
                        break;//跳出死循环,位置找到了,msg要插在p前面,此时prev->p,执行插入后期望的顺序为 prev->msg->p
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p;   //
                prev.next = msg;//这2步实现的功能就是链表顺序变为 prev->msg->p 此时头消息还是mMessage,但msg已经到它的链表上了
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);       //有消息来了,底层唤醒Looper
            }
        }
        return true;
    }

2,这样就很明了了,发送的消息经过比较和插入,终于进入到消息链表中了,结合前面的Looper.loop(),这样,整个流程就清楚了。

Summary: The general process is like this. At the initial stage of the main thread creation (ActivithThread.man()), while Looper is created, a message queue MessageQueue is created in the Looper construction method, and Looper is called after main(). loop(), loop() will call the Messagequeue instantiated in the Looper, continuously obtain the Message through queue.next(), and then schedule and process the message through the target (that is, the Handler) set by the message, and we can also use the instance A series of send/post methods of the optimized Handler to send messages to the message queue. Take a chestnut in reality:

As long as the subway station is running normally, the machines that have passed the security inspection must be turned on and running. The subway station can be compared to ActivityThread, the security inspection machine is Looper, and the security inspection equipment is running continuously (calling Looper.loop()). The security inspection equipment has a transmission belt ( MessgaQueue), each luggage (Message) must be placed on the conveyor belt, the process of putting it is the send/post() of the handler, after the luggage is tested and there is no problem, it can be taken away from the other end (handler.dispatchMessage(message)), If the handler.send/post() operation is executed in another thread, when the message is retrieved and scheduled for processing, it is already in the main thread, which realizes asynchronous callback.

 

The working principle of Handler is roughly as shown in the figure

In this way, the entire workflow of Handler is sorted out.

Of course, when it comes to infinite loops, why doesn't looper.loop() cause thread blocking due to infinite loops?

答案很简单,整个App因为主线程中loop.looper()的死循环而保持活动,如果跳出死循环,整个App会抛异常,
ActivityThread.main(){
...
    Looper.loop();//重点2
   throw new RuntimeException("Main thread loop unexpectedly exited");//抛异常, 主线程的loop出乎意料的退出了 这句话正常情况下不会被执行
}
而且,造成主线程堵塞是因为对Message处理的超时造成的,以之前举例说明,安检设备因为传输带不断循环而保持正常工作,一旦行李过重,用户从传送带上搬离行李过慢才会造成后续行李的卡住(各类事件不被处理,造成阻塞)。
同样的,在消息队列中没有消息时,主线程会进入休眠状态,即消息队列执行next()方式时,会根据下一个消息还有多久执行系统底层方法nativePollOnce()释放资源进入休眠状态
        MessageQueue.next(){
             for (;;) {
                if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);//直到下条消息到达或者有事务发生,
        }

而一旦有最新消息事件进入到消息队列时,一旦新事件需要马上执行或者到了执行时间的时间,会通过系统底层方法nativeWake()唤醒主线程进行工作
        MessageQueue.enqueueMessage(){

        ...
          if (needWake) {
                nativeWake(mPtr);//通过往 pipe 管道写端写入数据来唤醒主线程工作,这里采用的 epoll 机制
            }
        }

The sleep mechanism executed by the main thread when there are no tasks optimizes the performance to a great extent. Similarly, when sending messages, using obtain is more efficient than direct instances.

1,Message.java

  /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     *从全局池中返回一个新消息实例
     */
 public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;//Message类内部也有一个消息头,相当于也有一个消息链表 头消息置于m
                sPool = m.next;     //下一个消息置为表头 
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;//
                return m;//返回当初的表头消息
            }
        }
        return new Message();
    }

这样的话也不用每次都实例消息,最大限度的提高效率。
Summary: As a message callback mechanism, Handler involves relatively few associated classes, and the overall structure is relatively clear, but the internal logic is still very valuable to learn.

Guess you like

Origin blog.csdn.net/sunguanyong/article/details/127861061