天天看点

笔记:安卓App消息处理机制

类似Binder机制,MessageQueue、Looper也有底层的C++实现,涉及文件管道和驱动等。

以下仅从Java层的Looper、Handler和MessageQueue等相关类型的源码来分析线程消息处理的机制。

Looper用来创建和启动消息队列。

Looper对象和线程绑定是通过ThreadLocal实现的。

它提供了各种getter方便获取线程关联的MessageQueue和Looper对象。

线程通过执行Looper.prepare()来创建关联的MessageQueue。

主线程则调用prepareMainLooper()来创建主线程关联的Looper,方便其它线程向主线程发消息。

它新建了Looper对象,以ThreadLocal存储它,所以和当前线程绑定。

构造函数中创建了消息队列:

nativeInit()调用C++代码创建底层的C++MessageQueue对象。

有了MessageQueue对象以后,接着需要开启消息循环,使用关联的Handler来发送、处理消息了。

MessageQueue有点像一个阻塞队列,它提供MessageQueue.next()用来从队列中取出一个Message对象。

若没有消息则调用会阻塞。

Looper.loop()用来开启对MessageQueue的取消息操作的无限循环。

可以看到,loop()的操作就是无限从queue中调用next()获取一个新Message对象,然后执行dispatchMessage来处理它。

nativePollOnce()用来检查队列中是否有新的消息。

参数nextPollTimeoutMillis表示在暂无消息时此次检查过程需要休眠的时间:

等于-1:表示无限等待,直到被其它线程唤醒。

等于 0:继续循环检查队列。

大于 0:以nextPollTimeoutMillis的毫米数等待。

使用for (;😉 无限循环检查是否有消息,直到返回一个Message对象。

若当前MessageQueue正在退出,则返回null作为标识。

每次调用next()时,第一次检查如果发现没有要出来的消息,就一次性调用所有在注册了的IdleHandler回调对象。

MessageQueue和Looper对象都是和某个线程关联的。

向一个线程发送消息,通过和它绑定的一个Handler对象进行。

执行创建Handler对象的的线程,Handler对象和此线程绑定。

Handler对象的构造函数中,将当前线程关联的Looper和MessageQueue保存到其字段中。

每个发送到线程关联的MessageQueue中的Message对象,主要字段有:

long when字段表示消息预期被处理的时间;

int what表示消息的动作;

Object obj可以携带额外的数据。

发送消息的一般形式为:

若不指定when那么它为当前时间,之后被Looper取出后立即执行;

sendMessage()中将<code>Handler Message.target</code>设置为自身,最执行Handler绑定的队列的MessageQueue.enqueue()方法。

字段<code>Message Message.next</code>用来指向Message链表中的下一个对象。

MessageQueue.mMessages字段指向了它拥有的消息链表的头结点。

mMessages中的消息根据其when的时间排列,时间近的在前面。

enqueueMessage()在添加消息时将它放到mMessages的合适的位置。

mBlocked记录当前队列是否处于休眠?

在next()时若最近要处理的消息的when还未到或队列空时,则在检查队列是否空时会主动休眠一段时间。

若新的Message被添加到链表头,且它的when时间到了,那么就唤醒Looper继续执行next(),获取此Message然后处理它。

在Looper.loop()中,每当获取新的消息后:

target就是发送Message的Hander。

Hander发送消息到其绑定的MessageQueue中。

可见,相关的Looper、MessageQueue、Handler都是和同一个线程关联的。

从next()的执行在Looper.loop()的循环中进行可知:

Hander.dispatchMessage()的调用就是在Looper关联的线程中进行。

上面的Message.callback是一个Runnable。

Handler.mCallback是创建它时指定的一个回调对象。

handleMessage()就是子类重写处理自定义消息的地方。

Looper.loop()执行后,线程“阻塞”,不断从关联的MessageQueue中取出消息并处理。

其它线程或在处理某个消息的逻辑中,可以调用Looper.quit()退出当前线程的消息循环。

它执行了MessageQueue.quit()。

这里设置mQuitting为true。

之后下次调用next()时,返回null告知Looper.loop()退出循环。

线程的消息循环结束。

(本文使用Atom编写)