Android源码分析之MessageQueue
Android源码分析之MessageQueue
下面让我们花些时间来看看MessageQueue的具体实现,不过在分析代码之前让我们来理解下在类开头的一大段comments。
MessageQueue是比较低层的类,是持有Message(在Looper中派发)的队列,但Message不是直接添加到MessageQueue中的,
而是通过与Looper相关联的Handler来进行的。大多数情况下,你不需要显式的new它,当你setup一个Looper时,MessageQueue
会被自动创建(具体参见Looper的构造器)。
下面我们先来看看关键字段和ctor:
"unused" mPtr;
ArrayList<IdleHandler> mIdleHandlers = ArrayList<IdleHandler>
nativeDestroy( nativePollOnce( ptr, nativeWake( nativeIsIdling(==
mQuitAllowed表示MessageQueue是否允许退出,系统创建的UI线程的MessageQueue是不允许的,其他客户端代码创建的都是允许的;
mPtr是native代码相关的,指向C/C++代码中的某些对象(指针),其他一些nativeXXX()相关的函数本文暂不做分析;
mMessages表示消息队列的头Head;
mIdleHandlers是IdldHandler接口的ArrayList, mPendingIdleHandlers是数组版本,在后面的代码中会将ArrayList的内容拷贝到它里面;
mQuitting表示当前队列是否处于正在退出状态;
mBlocked表示next()调用是否被block在timeout不为0的pollOnce上;
mNextBarrierToken表示下一个barrier token,barrier用target==null, arg1==token的Message对象表示;
ctor中初始化mQuitAllowd和native的mPtr指针;
下面让我们接着看下IdleHandler callback接口:
(handler == NullPointerException("Can't add a null IdleHandler" (
(
IdleHandler接口表示当MessageQueue发现当前没有更多消息可以处理的时候则顺便干点别的事情的callback函数(即如果发现idle了,
那就找点别的事干)。callback函数有个boolean的返回值,表示是否keep。如果返回false,则它会在调用完毕之后从mIdleHandlers
中移除。这里让我们来看一个具体的例子(实现),ActivityThread.java里的一个内部类,代码如下:
GcIdler
这是一个gc相关的IdleHandler,即如果没有更多的消息可以处理就会抽空doGcIfNeeded(),最后返回false表示不保留在mIdleHandlers
中,即用一次就扔了,只执行一遍。
下来看一组清理,销毁相关的方法:
finalize()
(mPtr != 0= 0
dispose会在finalize被调用的时候执行,会清理掉native层的相关对象。
接下来看MessageQueue的核心方法,next()方法,如下:
pendingIdleHandlerCount = -1;
nextPollTimeoutMillis = 0 (nextPollTimeoutMillis != 0
(
now == = (msg != && msg.target ==
== (msg != && ! (msg != (now <
nextPollTimeoutMillis = () Math.min(msg.when -
mBlocked = (prevMsg != === () Log.v("MessageQueue", "Returning message: " +
nextPollTimeoutMillis = -1
(pendingIdleHandlerCount < 0
&& (mMessages == || now <= (pendingIdleHandlerCount <= 0
mBlocked = (mPendingIdleHandlers == = IdleHandler[Math.max(pendingIdleHandlerCount, 4=
( i = 0; i < pendingIdleHandlerCount; i++ IdleHandler idler == ;
keep = ="MessageQueue", "IdleHandler threw exception" (! (
pendingIdleHandlerCount = 0
nextPollTimeoutMillis = 0
首先初始化2个接下来要用到的变量,紧接着进入无限for循环中,其某次循环主要做这么几件事情:
1. 如果nextPollTimeoutMillis != 0的话,调用Binder.flushPendingCommands();
2. 调用nativePollOnce(mPtr, nextPollTimeoutMillis);
3. 进入一个大的同步块,尝试获取一个可以处理的消息,具体做法是,记录当前时间now,初始化变量prevMsg为null,msg为mMessges;
如果msg是一个barrier消息,则搜索消息队列,直到找到一个asynchronous的消息,同时更新prevMsg,msg的值;当退出此do...while
循环的时候msg可能为空(走到队列尾了),或者成功找到了一个这样的(异步)消息。如果是到队尾了即msg==null,则表示没更多的消息
了,设置nextPollTimeoutMillis = -1;否则当now<msg.when(msg的时间还没到),设置一个合理的等待时间,即调用
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);当msg到了该处理的时间了,也就是说我们找到了
这样一个消息可以返回了,设置mBlocked为false,将msg从mMessages队列中取出来(类似单链表的删除操作),并执行
msg.next=null、msg.markInUse(),返回msg。如果到这一步了还没return的话,那说明还没有可以处理的消息,检查下队列是否要求
退出了,如果是执行dispose(),返回null。当Looper的loop方法看到null的message的时候会退出loop。接下来既然没消息可以处理,
那就该处理IdleHandler了。如果pendingIdleHandlerCount小于0(注意其在第一次进入for循环是被初始化为-1)且没更多的消息需要
处理,设置pendingIdleHandlerCount=mIdleHandlers.size();如果pendingIdleHandlerCount还是<=0的话,表示没有idle handler
需要执行,设置mBlocked为true,接着进入下次循环。接下来就是根据mIdleHandlers来初始化mPendingIdleHandlers。
4. 退出同步块后我们就剩下最后一件事了,那就是run Idle handlers。一个for循环用来做这就事情,在循环内如果IdleHandler没必要保留,
则会从mIdleHandlers中移除。
5. 最后重置pendingIdleHandlerCount为0(也就是4只会在第一次循环的时候执行一次),将nextPollTimeoutMillis设为0,因为当我们在
执行4的时候,新的Message可能已经到来了,所以我们需要立即开始(不需要等待)下次循环来检查。
看完了next()方法,接下来我们来看enqueue()方法:
enqueueMessage(Message msg, AndroidRuntimeException(msg + " This message is already in use." (msg.target == AndroidRuntimeException("Message must have a target." (= + " sending message to a Handler on a dead thread""MessageQueue" == (p == || when == 0 || when <
msg.next ===
needWake = mBlocked && p.target == &&== (p == || when < (needWake &&= = p;
prev.next =
在介绍Handler的那篇文章中我们看到,许多跟message(包括runnable)相关的操作,最终都delegate给了MessageQueue的enqueue
方法。和任何方法一样,在enqueue之前,也都是对参数msg的检查,比如msg如果在使用中,或者msg的target是null,都会抛出
AndroidRuntimeException,进行完条件检查后,会进入真正的处理逻辑。接下来的操作类似在一张单链表中插入一个元素:进入同步块
1. 如果此队列处于正在退出的状态则不能在往里入队了,不能插入元素了,在这种情况下会抛出RuntimeException,然后return false,
表示失败了;
2. 接下来表示队列的状态ok,设置msg的when字段,临时变量p指向队列头;(必要的初始化,准备工作)
3. 如果队列是空的或when==0或when<p.when,也就是说要插入的这个message应该在第一个位置也就是队首,那么它将是新的Head,
将它和原先的队列连接起来;否则插入将发生在队列中间的某个位置,将msg插在第一个p的前面,p要么是null要么满足msg.when<p.when。
最后退出同步块,返回true,表示操作(入队)成功。
接下来看2个hasMessages()方法:
hasMessages(Handler h, (h == (= (p != (p.target == h && p.what == what && (object == || p.obj == = (h == (= (p != (p.target == h && p.callback == r && (object == || p.obj == =
这2个方法都是根据message中的字段(target、what、callback、obj)来查找相应的message。唯一要注意的就是如果h为null的话,
方法直接返回false,没有更多操作;还有一点要注意的是如果object传null的话,则忽略匹配obj字段。
接着我们来看3个类似的removeMessages()方法,
removeMessages(Handler h, (h == (=
(p != && p.target == h && p.what ==&& (object == || p.obj =====
(p != = (n != (n.target == h && n.what ==&& (object == || n.obj ===== (h == || r == (=
(p != && p.target == h && p.callback ==&& (object == || p.obj =====
(p != = (n != (n.target == h && n.callback ==&& (object == || n.obj ===== (h == (=
(p != && p.target ==&& (object == || p.obj =====
(p != = (n != (n.target == h && (object == || n.obj =====
这3个方法只是remove的条件不同,其主逻辑都是相同的,即从队列中删除匹配的元素。总体思想都是先从队首删除,如果删除了则队首
指向接下来的元素(排在第2位置的),重复这个过程,直到第一个不匹配的元素出现。接着从这个元素之后(after front)开始查找并删除,
方法是前一个元素指向它后一个的后一个,即p.next=nn。注意这里都是删除所有匹配的消息,而不是第一个匹配的。
最后看下队列的quit()方法:
quit( (! RuntimeException("Main thread not allowed to quit." (=
= (p != === now == (p != (p.when >= (n == (n.when >== == (n !=
quit方法根据所传参数safe的值,有2种不同的退出策略,如果是safe的退出,则执行removeAllFutureMessagesLocked(),
其内部的逻辑为如果队首的元素还没到期那说明队列中其他所有的元素也都没到期,所以等同于删除所有的消息即调用
removeAllMessagesLocked();否则遍历队列找到第一个p.when>now这样的message元素p,(更新队列的尾部p.next=null,
缩短队列)从p开始一直到队列结束都是要被删掉的元素,全部删除之;如果是unsafe的退出,则所有message都直接被删除并
回收即调用removeAllMessagesLocked()。
至此,MessageQueue的绝大部分关键代码都已分析完毕,其实你可以很明显的感觉到其内部很多操作都和单链表的操作一致,所以
理解起来并不难。我的一点小技巧,针对这种指针多的操作,你不妨在纸上画画草图,有时候会非常清晰,特别利于链表类操作的理解。
用户评论