Handler的方方面面

482 阅读18分钟

Handler是我们再熟悉不过的概念,我们经常使用它来进行子线程与主线程之间的通信。它又如此的重要,以至于HandlerThread,EventBus,RxJava,Glide等等开源框架都有它的身影。

这篇文章主要分析Handler及其相关类Looper、Message和MessageQueue等工作流程,还会讲到native层机制,内存屏障,IdleHandler等。

源码分析

我们从平时使用Handler的代码开始分析Handler底层的逻辑

        // 只用来演示,不简易这样使用,会导致内存泄漏
        val handler = @SuppressLint("HandlerLeak")
        object : Handler(){
            override fun handleMessage(msg: Message) {
                super.handleMessage(msg)
                // todo 处理消息
            }
        }
        val message = Message.obtain()
        message.what = 1
        handler.sendMessage(message)

        handler.post(object : Runnable{
            override fun run() {
                // todo 处理消息
            }
        })

使用方式很简单,主要有下面4步:

  1. 创建Handler实例,重写handleMessage方法
  2. 创建要发送的Message实例
  3. 发送消息,可以使用sendMessage,也可以使用post方式
  4. 接收到消息
    • 如果是sendMessage发送的消息则在handleMessage中接收到
    • 如果是post发送的消息,则会回调Runnable的run方法

下面会详细的对这4个步骤进行源码分析

1. 创建Handler实例

先从handler的构造函数开始,我们平时使用最多的就是无参构造,实际内部又调用的是有参构造。默认callback赋值为null,async赋值为false。

我们也可以选择有参构造

  • Callback是Handler内部的回调接口,如果设置了则派发消息时,handleMessage会被回调。
  • async表示是否是异步消息,默认是false,表示是同步消息
  • Looper是MessageQueue中Message的管理者,具体用处后面再讲
 public Handler() {//无参构造
        this(null, false);
    }

 public Handler(Callback callback, boolean async) {
      ...
        mLooper = Looper.myLooper();// 获取looper对象
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;// 获取queue对象
        mCallback = callback;// handler无参构造时callback为null
        mAsynchronous = async;
    }

 public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    ...
   public interface Callback {
              boolean handleMessage(@NonNull Message msg);
    }
    
    /**
     * Subclasses must implement this to receive messages
     * 子类一定要实现该方法来接收消息
     */
    public void handleMessage(@NonNull Message msg) {
    }

2. 创建Message

Message是单列表结构,多个Message通过next引用连接起来,用于实现消息池。因为android会频繁的使用Message的对象,使用“池”这种机制可以减少创建对象开辟内存的时间,更加高效的利用内存。

public final class Message implements Parcelable {
    public int what;
    public Object obj;
    public int arg1; 
    public int arg2;
    ……
    public long when; // 消息执行的时间戳
    Handler target;  //每个消息都有一个成员保存和他关联的Handler
    Runnable callback;// 回调,用于post发送消息的方式
    Message next;//Message是单链表结构,指向下一个Message
    public static final Object sPoolSync = new Object();// 锁对象
    private static Message sPool;// 静态对象,消息池的head节点
    private static int sPoolSize = 0;// 消息池的大小
    private static final int MAX_POOL_SIZE = 50; // 消息池的最大容量
    
    public static Message obtain() {
        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();
    }
}

建议使用obtain来创建Message实例,优先从池内取出对象,先判断头节点sPool是否为空,不为空则返回;为空则说明当前池内没有节点,则创建新的Message对象。

Message使用完后需要调用recycle进行手动回收,类似于Bitmap的回收。

public void recycle() {
        ... 
        recycleUnchecked();
    }

    @UnsupportedAppUsage
    void recycleUnchecked() {
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = UID_NONE;
        workSourceUid = UID_NONE;
        when = 0;
        target = null;
        callback = null;
        data = null;
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;// 把当前message插入到最前面
                sPool = this;
                sPoolSize++;
            }
        }
    }

将Message中的成员变量赋值为初始值,如果消息池的大小没有超过最大容量(50),则放入消息池中进行回收。

3. 发送消息

Handler发送消息有两种方式:

  1. sendMessage
  2. post

另外还可以发送延迟消息:

  1. sendMessageDelayed
  2. postDelayed

3.1 sendMessage

我们先看下sendMessage方法

 public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

 public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        // 注意当前时间用的是SystemClock.uptimeMillis(),而不是System.currentTimeMillis
        // 第二个参数为消息执行的时间,由当前时间+延迟的时间组成,不延迟时delayMillis为0
       return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        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;// 重要:msg的target设置为该handler对象
        if (mAsynchronous) {// 不设置默认是false,则该消息为同步消息
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

sendMessage方法通过层层调用,最后调用了enqueueMessage方法,在这个方法里,先是给msg的target设置为该handler对象,默认mAsynchronous为false,则消息为同步消息。关于同步消息和异步消息的用不后面再讲。

最后调用了queue的enqueueMessage方法。

System.currentTimeMillis()与SystemClock.uptimeMillis().md

3.2 post

 public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

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

原来post内部也是通过sendMessageDelayed的方式实现的。通过getPostMessage创建Message对象,不同点在于将我们创建的Runable对象作为callback属性,赋值给了此message,该callback会在派发消息的时候回调

发送消息的两种方式讲完了,消息如何进入MessageQueue的,需要看下MessageQueue类的enqueueMessage方法

3.3 MessageQueue#enqueueMessage

 boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.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;
                mMessages = msg;
                needWake = mBlocked;// 如果blacked为ture则表示需要唤醒
            } else {
                // Inserted within the middle of the queue. Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();// 重要
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {// 找到了when比当前Message的when大的Message
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                // 把消息插到前面
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

整个enqueueMessage方法的过程就是先持有MessageQueue.this锁,然后将Message放入队列中,放入队列的过程是:

  1. 如果队列为空,或者当前处理的时间点为0(when的数值,when表示Message将要执行的时间点),或者当前Message需要处理的时间点先于队列中的首节点,那么就将Message放入队列首部,否则进行第2步。由于是插入到首节点,如果此时阻塞了,则需要唤醒。

  2. 遍历队列中Message,找到when比当前Message的when大的Message,将Message插入到该Message之前,如果没找到则将Message插入到队列最后。所以消息队列中根据消息执行时间消息是从小到大排列的。插入到队列中间一般不需要唤醒,除非设置了同步屏障并且该消息是最早的异步消息。

  3. 判断是否需要唤醒,一般是当前队列为空的情况下,next那边会进入睡眠,需要enqueue这边唤醒next函数。后面会详细介绍

4. 接收消息

我们发送到MessageQueue中的消息是怎么被我们接收到的呢?答案是Looper。Looper作为MessageQueue的管理者,不停的在MessageQueue中找到要处理的Message并发送给我们,这样我们就接收到了。

既然需要Looper,但是我们平时使用的时候,为什么不需要创建Looper实例呢? 那是由于app启动创建主线程(ActivityThread)的时候,在main方法中已经帮我们创建了Looper对象,并且调用了loop()方法。所以我们平时在主线程使用的时候就不需要手动创建了。

...
Looper.prepareMainLooper();
...
Looper.loop();
...

但是在子线程使用的话,就需要手动调用prepare创建Looper实例,并调用loop方法。那是由于Looper是和线程绑定的,当线程中没有Looper对象的时候,就需要我们手动创建。

我们先来看下Looper对象的创建过程,Looper的构造函数是私有的,Looper对象必须通过prepare方法来创建。 创建好的Looper对象被保存在TheadLocal中,作用是为了保证每个线程都只有一个Looper对象。此外在Looper构造函数中创建了MessageQueue对象。

 public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {// 一个线程只有一个Looper对象
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));// 创建Looper对象,并存放在ThreadLocal中
    }

  private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);// 创建MessageQueue对象
        mThread = Thread.currentThread();
    }

Looper对象是和线程绑定在一起的,一个线程对应着一个Looper,并且一个Looper有对应着一个MessageQueue。

我们再来看下MessageQueue的构造函数,构造函数很简单,最重要的就是调用了nativeInit(),这个函数后面还会再讲,现在先简单介绍下,nativeInit是主要用来进行native层初始化,返回native层的nativeMessageQueue的指针mPtr,这个指针后面还会用到。

// MessageQueue的构造函数
MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();// native层的初始化,返回native层的nativeMessageQueue的指针
    }

我们再看下Looper的loop方法

 public static void loop() {
        final Looper me = myLooper();// 拿到当前线程的Looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;// 拿到Looper中的MessageQueue

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            // 通过MessageQueue的next()方法拿消息,可能就阻塞在这里
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
                msg.target.dispatchMessage(msg);// 调用发送消息的handler的dispatchMessage来处理消息
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (slowDispatchThresholdMs > 0) {
                final long time = end - start;
                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();//回收
        }
    }

拿到当前线程所绑定的Looper对象中的queue,在for死循环中通过queue.next()方法拿到要处理的Message,当没有需要处理的消息时,则阻塞在queue.next()处;如果拿到了消息会被唤醒,则调用msg.target.dispatchMessage(msg),target是Message绑定的Handler对象,此时就把Message交给了Handler来处理,具体处理流程后面再讲。

接着看queue.next()是怎么处理的,消息是怎么一个个被取出来的。

 Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;//mPrt是native层的MessageQueue的指针
       if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);//重要

            synchronized (this) {
                // Try to retrieve the next message. Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {//target 正常情况下都不会为null,在postBarrier会出现target为null的Message
                   // Stalled by a barrier. Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());// 遍历queue找到异步消息
                }
                if (msg != null) {
                    if (now < msg.when) {// 消息的时间大于当前时间,不需要处理,计算下次唤醒的时间
                        // Next message is not ready. Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;// 消息时间小于当前时间,需要处理该消息,则返回
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;// 没有消息,-1表示等待时间无限长
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                // queue中没有msg,或者msg还没到可执行的时间
                // 那么线程就处于空闲了,就可以执行IdleHandler了
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run. Loop and wait some more.
                    mBlocked = true;// 置为ture
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            // 执行IdleHandler
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();// 执行
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        // 如果之前的idler.queueIdle()返回false,则执行完后移除掉
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            // 
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

整个next函数的主要是执行步骤是:

  1. 初始化操作,如果mPtr为null,则直接返回null,设置nextPollTimeoutMillis为0,进入下一步。

  2. 调用nativePollOnce, nativePollOnce有两个参数,第一个为mPtr表示native层MessageQueue的指针,nextPollTimeoutMillis表示超时返回时间,调用这个nativePollOnce会阻塞的此处等待唤醒,如果超过nextPollTimeoutMillis时间,则不管有没有被唤醒都会返回。-1表示一直等待,0表示立刻返回。下一小节单独介绍这个函数。

  3. 获取队列的头Message(msg),如果头Message的target为null,则表示发送了同步屏障,则优先查找一个异步Message来进行下一步处理。当队列中添加了同步Barrier的时候target会为null。

  4. 判断上一步获取的msg是否为null,为null说明当前队列中没有msg,设置等待时间nextPollTimeoutMillis为-1。实际上是等待enqueueMessage的nativeWake来唤醒,执行step4。如果非null,则下一步

  5. 判断msg的执行时间(when)是否比当前时间(now)的大,如果小,则将msg从队列中移除,并且返回msg,结束。如果大则设置等待时间nextPollTimeoutMillis为(int) Math.min(msg.when - now, Integer.MAX_VALUE),执行时间与当前时间的差与MAX_VALUE的较小值。执行下一步

  6. 判断是否MessageQueue是否已经取消,如果取消的话则返回null,否则下一步

  7. 运行idle Handler,idle表示当前有空闲时间的时候执行,而运行到这一步的时候,表示消息队列处理已经是出于空闲时间了(队列中没有Message,或者头部Message的执行时间(when)在当前时间之后)。如果没有idle,则继续step2,如果有则执行idleHandler的queueIdle方法,我们可以自己添加IdleHandler到MessageQueue里面(addIdleHandler方法),执行完后,回到step2。

回过头来看下Handler#dispatchMessage方法:

 public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);// 调用runnable.run()方法
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {// 如果callback不为null,则调用callback的handlerMessage
                    return;// 返回
                }
            }
            handleMessage(msg);// 最后才会调用到handleMesaage方法
        }
    }
  private static void handleCallback(Message message) {
        message.callback.run();
    }

第2行,如果msg.callback不为null,则执行callback回调,msg.callback就是通过post发送消息传递的Runnable对象

Handler.Callback 有优先处理消息的权利 ,当一条消息被 Callback 处理并拦截(返回 true),那么 Handler 的 handleMessage(msg) 方法就不会被调用了;如果 Callback 处理了消息,但是并没有拦截(返回false),那么就意味着一个消息可以同时被 Callback 以及handleMessage 处理。

如果mCallback为null,则只会调用handleMessage方法

到这里,我们终于整个流程分析完了。

native机制

nativePollOnce与nativeWake底层使用了linux中的pipe管道来进行进程间的通讯,通过epoll机制实现的等待和唤醒。

管道是Linux系统中的一种进程间通信机制,具体可以参考前面一篇文章Android学习启动篇推荐的一本书《Linux内核源代码情景分析》中的第6章--传统的Uinx进程间通信。简单来说,管道就是一个文件,在管道的两端,分别是两个打开文件文件描述符,这两个打开文件描述符都是对应同一个文件,其中一个是用来读的,别一个是用来写的,一般的使用方式就是,一个线程通过读文件描述符中来读管道的内容,当管道没有内容时,这个线程就会进入等待状态,而另外一个线程通过写文件描述符来向管道中写入内容,写入内容的时候,如果另一端正有线程正在等待管道中的内容,那么这个线程就会被唤醒。这个等待和唤醒的操作是如何进行的呢,这就要借助Linux系统中的epoll机制了。 Linux系统中的epoll机制为处理大批量句柄而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著减少程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。但是这里我们其实只需要监控的IO接口只有mWakeReadPipeFd一个,即前面我们所创建的管道的读端,为什么还需要用到epoll呢?有点用牛刀来杀鸡的味道。其实不然,这个Looper类是非常强大的,它除了监控内部所创建的管道接口之外,还提供了addFd接口供外界面调用,外界可以通过这个接口把自己想要监控的IO事件一并加入到这个Looper对象中去,当所有这些被监控的IO接口上面有事件发生时,就会唤醒相应的线程来处理,不过这里我们只关心刚才所创建的管道的IO事件的发生。 Android应用程序消息处理机制(Looper、Handler)分析

  • nativeInit()方法:
    • 创建了NativeMessageQueue对象,增加其引用计数,并将NativeMessageQueue指针mPtr保存在Java层的MessageQueue
    • 创建了Native Looper对象
    • 调用epoll的epoll_create()/epoll_ctl()来完成对mWakeEventFd和mRequests文件描述符的可读事件监听
  • nativeDestroy()方法
    • 调用RefBase::decStrong()来减少对象的引用计数
    • 当引用计数为0时,则删除NativeMessageQueue对象
  • nativePollOnce()方法
    • 调用Looper::pollOnce()来完成,空闲时停留在epoll_wait()方法,用于等待事件发生或者超时
  • nativeWake()方法
    • 调用Looper::wake()来完成,向管道mWakeEventfd写入字符,等待的线程会被唤醒;

MessageQueue通过mPtr变量保存NativeMessageQueue对象,从而使得MessageQueue成为Java层和Native层的枢纽,既能处理上层消息,也能处理native层消息;下面列举Java层与Native层的对应图

同步屏障

handler中消息分为同步消息和异步消息,我们平时使用sendxx或者postxx方法发送的消息都是同步消息。

同步屏障就是同步消息的屏障,让异步消息优先执行。在MessageQueue#next方法的时候已经分析过。

典型的应用场景是:

Android应用框架中为了更快的响应UI刷新事件在ViewRootImpl.scheduleTraversals中使用了同步屏障

 void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            // 设置同步障碍,确保mTraversalRunnable优先被执行
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            // 内部通过Handler发送异步消息
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
Choreographer.postCallback
    ->postCallbackDelayed
        ->postCallbackDelayedInternal

private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long delayMillis) {
                ...
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
                msg.setAsynchronous(true);// 异步消息
                mHandler.sendMessageAtTime(msg, dueTime);
        }
    }

mTraversalRunnable调用了performTraversals执行measure、layout、draw

为了让mTraversalRunnable尽快被执行,在发消息之前调用MessageQueue.postSyncBarrier设置了同步屏障

下面看下MessageQueue#postSyncBarrier:

 public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }

    private int postSyncBarrier(long when) {
        // Enqueue a new sync barrier token.
        // We don't need to wake the queue because the purpose of a barrier is to stall it.
        synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;

            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                // 循环消息链表,找到第一个when大于当前时间的消息p
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            // 把msg插入到p前面
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

创建了Message实例,并将它按照when从小到大顺序插入到合适的位置。

同步屏障消息和普通消息最大的不同的是没有设置target,也就是target=null。所以在MessageQueue#next遍历的时候,target=null的消息就是同步屏障。

Handler之同步屏障机制(sync barrier)

IdleHandler

IdleHandler会线程消息队空闲时执行,可以用来提升性能。

比如在LeakCanary中使用IdleHandler在空闲的时候执行GC操作,监测对象没有被回收。

具体执行过程在MessageQueue#next中也已经分析过了,下面看下具体用法:

Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
            @Override
            public boolean queueIdle() {
                // todo
                return false;
            }
        });

queueIdle返回值决定着IdleHandler会被执行一次还是多次,如果返回false,则执行完后便会被移除掉。

面试问题

1. 主线程的死循环一直运行是不是特别消耗CPU资源呢?

并不是,这里就涉及到Linux pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质是同步I/O,即读写是阻塞的。所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。

总结

借用一张图总结下Handler的流程

Android MessageQueue源码分析

Android消息机制2-Handler(Native层)

Handler 都没搞懂,拿什么去跳槽啊?