【17】Android 线程间通信(二) - Handler

avatar
作者
筋斗云
阅读量:2

概述

记得上篇文章我们留下的问题吗?如果还没有看过上一篇讲解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这个线程间通信的方式,底层到底还做了什么。

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!