概述
记得上篇文章我们留下的问题吗?如果还没有看过上一篇讲解Handler基本原理文章的同学可以补一下知识。Android 线程间通信(一) - Handler
回到正题,这次我们将一下上一篇文章留下问题中的几个,idleHandler & syncBarrier。
同步屏障
首先我们了解吗,我们平时通过Handler#sendMessage发送的属于什么消息?同步消息还是异步消息。既然这次讲到同步屏障,我们也可以大胆的猜想,我们平时发送的消息是同步消息,只有特殊情况下,插入这个同步屏障,才会走特殊的异步方式。
如果能够这样推导,说明在Handler这一块的了解已经比较深刻了。我们继续看一下源码。
//MessageQueue.java @UnsupportedAppUsage Message next() { ... 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; //(1) msg不为空,且msg.target为空才会进入 if (msg != null && msg.target == null) { // Stalled by a barrier. Find the next asynchronous message in the queue. do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } 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)注解的位置,msg不为空这个很好理解,消息需要有实体,但是target为空这个是为什么呢?上一片文章讲过,msg的target其实就是Handler。而下面官方的注解说明,被栏栅挡住了,在队列中查找下一个异步消息。然后开始从当前这个msg的下一个msg开始查找,知道下一个消息属于异步消息或者查找直到为空位置,跳出循环。
可见挡住同步消息的,正是这个msg.target=null的消息,那么它是怎么插入进来的呢?我们接着看。
//MessageQueue.java @UnsupportedAppUsage @TestApi 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) { while (p != null && p.when <= when) { prev = p; p = p.next; } } if (prev != null) { // invariant: p == prev.next msg.next = p; prev.next = msg; } else { msg.next = p; mMessages = msg; } return token; } }
MessageQueue中有一个方法叫做postSyncBarrier,可以理解为插入同步屏障。首先通过Message#obtain获取消息,这里是对消息对象的一个复用,在消息处理完之后,会把Message的target清除。然后标记为使用,接着就是消息的一些属性赋值,但唯独没有重新确定msg的target对象。可见如果messageQueue需要插入同步屏障,就是通过这个方法把屏障消息插入进去的。
插入同步屏障消息是直接通过MessageQueue进行插入
既然有插入同步屏障,那么也有移除屏障,让同步消息可以继续执行。
//MessageQueue.java @UnsupportedAppUsage @TestApi public void removeSyncBarrier(int token) { // Remove a sync barrier token from the queue. // If the queue is no longer stalled by a barrier then wake it. synchronized (this) { Message prev = null; Message p = mMessages; while (p != null && (p.target != null || p.arg1 != token)) { prev = p; p = p.next; } if (p == null) { throw new IllegalStateException("The specified message queue synchronization " + " barrier token has not been posted or has already been removed."); } final boolean needWake; if (prev != null) { prev.next = p.next; needWake = false; } else { mMessages = p.next; needWake = mMessages == null || mMessages.target != null; } p.recycleUnchecked(); // If the loop is quitting then it is already awake. // We can assume mPtr != 0 when mQuitting is false. if (needWake && !mQuitting) { nativeWake(mPtr); } } }
还有个问题,就是同步屏障是挡住同步消息,那么怎么发送异步消息呢?我们看看Message的源码。
// Message.java public void setAsynchronous(boolean async) { if (async) { flags |= FLAG_ASYNCHRONOUS; } else { flags &= ~FLAG_ASYNCHRONOUS; } }
有一个设置异步的方法,根据传入的bool值,flags和标志位做位运算。我们只要了解,当async为true,flags = 1,async为false,flags = 0。之前跳出next中的do…while循环中,判断异步消息的方式,就是msg#isAsynchironous。也就是说,setAsynchronous当传入true的时候,判断消息的条件就为真,此时发送的消息就是异步消息了。
//Message.java public boolean isAsynchronous() { return (flags & FLAG_ASYNCHRONOUS) != 0; }
默认情况下flags = 0,为同步消息
这样一来,同步屏障的作用和使用方式,我们就彻底搞懂了。
IdleHandler
空闲Handler,和字面意思一样。Handler是消息处理的句柄,那么IdleHandler,就是处理空闲消息的句柄。在MessageQueue#next源码中,我们继续看一下。
//MessageQueue#next // 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. 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; 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. 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) { 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方法下面有这样一段代码,什么时候会执行下去呢?就是当msg=null的时候,因为当msg不为空,说明取出了msg,并返回给Looper进行后续分发了。msg为null,说明此时消息队列中取不出消息了,就会从mIdlehandlers这个集合中,获取集合大小,并且把集合转换成数组mPendingIdleHandlers。
接着通过一个for循环,取出每一个idler并执行它的queueIdle,这个queueIdle是一个接口,可见是一个接口回调的操作,回调给用户去操作了。
//MessageQueue.java public void addIdleHandler(@NonNull IdleHandler handler) { if (handler == null) { throw new NullPointerException("Can't add a null IdleHandler"); } synchronized (this) { mIdleHandlers.add(handler); } }
通过MessageQueue的addIdleHandler接口,我们可以向MessageQueue维护的集合中添加这种空闲Handler,当消息队列MessageQueue的消息队列中没有任务可以执行之后,就会执行这些空闲的消息。
可以发现,IdleHandler适合处理那些优先级比较低的事情,而不会影响到用户界面的响应性。避免在主线程繁忙的时候执行耗时任务,从而保持应用的流畅性。
总结
1、MessageQueue可以发送同步屏障消息
2、Message可以通过设置确认发送的消息为同步还是异步
3、同步屏障消息的Handler为空
4、MessageQueue可以添加IdleHandler来处理优先级较低的任务
这下应用层Handler的东西我们都了解了。下一篇文章接下来将通过native层来分析一下Handler这个线程间通信的方式,底层到底还做了什么。