前几篇我们学习了Handler的创建,Looper和MessageQueue是组合关系,Handler持有Looper和MessageQueue的引用。还有消息的创建与发送,消息持有Handler的引用,通过Handler发送到MessageQueue。同时了解到MessageQueue是单向链表,以Message的分发时间排序。
这一篇我们要探究Looper是如何循环遍历MessageQueue的,还有Looper是如何把消息分发给Handler的。
先来看看Looper.loop()的主要代码:
public static void loop() {
final Looper me = myLooper(); //获得Looper实例
...... //Looper空判断和判断是否正在循环
me.mInLoop = true; //将正在循环标志位设置为true
final MessageQueue queue = me.mQueue; //获得MessageQueue实例
......
for (;;) { //死循环遍历MessageQueue,不断拿出消息发送给Handler处理
Message msg = queue.next(); // 当没有消息返回时会阻塞
if (msg == null) { //返回消息为空时直接退出该循环
// No message indicates that the message queue is quitting.
return;
}
......
try {
msg.target.dispatchMessage(msg); //消息通过持用的Handler引用进行分发
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
......
msg.recycleUnchecked(); //回收该消息到全局消息池,下次可以直接通过obtain()方法获取消息,减少消息创建的开销
}
}
Loop.looper()方法内有一个死循环,该循环调用MessageQueue.next()方法来获得下一个待处理的消息,当没有消息返回时会阻塞等待。如果获得的消息为空,则直接结束循环。否则会通过该消息的Handler引用分发该消息并进行回收。
函数在调用实例变量和类变量时,往往会创建新的局部变量进行保存,规避函数结束之前被调用变量发生改变所带来的影响。
来看看MessageQueue.next()的具体代码:
Message next() {
// 当消息循环已退出时返回null
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; //空闲处理程序计数器,初始值为-1,大部分时候值为0
int nextPollTimeoutMillis = 0; //阻塞等待时间
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands(); //阻塞前将当前线程中挂起的所有Binder命令刷新到内核驱动程序
}
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) {
// 如果队头是消息屏障,则往下找到第一条异步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 如果还没到消息的分发时间,计算阻塞时长
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 获取消息,将消息从消息队列删除并标记为正在使用
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;
}
// 子线程的Looper调用了quit(),该标志位会被置为true
if (mQuitting) {
dispose();
return null;
}
//pendingIdleHandlerCount < 0条件只有第一次循环执行到这里时会成立
//当第一次执行到这里,且消息队列为空或者还没到消息的发送时间时,会执行空闲等待程序
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
//pendingIdleHandlerCount唯一一次大于0的机会,此处进行赋值,下面的空闲等待程序代码块才有机会执行
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// 空闲处理程序计数器不大于0,结束本次循环,开始下次循环进行等待阻塞
mBlocked = true;
continue;
}
//获取空闲等待程序
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 执行空闲等待程序
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);
}
}
}
pendingIdleHandlerCount = 0;
// 执行完空闲等待程序后,不会再进行阻塞等待,所以将阻塞时间置为0
nextPollTimeoutMillis = 0;
}
}
消息队列的loop()方法内也有一个死循环,从队头开始遍历消息。如果第一次遍历时队列为空或者还没到信息的分发时间,则会获取空闲等待程序进行执行。从第二次遍历开始会直接计算阻塞时间进行阻塞等待。获取消息时,直接取队头信息,如果消息为空,则将循环等待时间置为-1(参考动画的循环次数,这里大概是一直等待,直到被唤醒)。如果遇到屏障消息,会一直往下遍历直到找到异步消息,否则将一直等待下去。如果消息不为空且为同步消息,会先判断是否可以分发,如果未能分发将计算阻塞时间进行等待,可以分发的消息会直接取出发送给Handler处理,消息队列也会进行更新。loop()方法的退出条件是调用quit()。
消息的分发处理dispatchMessage(@NonNull Message msg)的源码:
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) { //消息自带的处理程序不为空则,会直接调用自带的处理方法
handleCallback(msg);
} else {
if (mCallback != null) { //mCallback是Handler初始化时传入的消息处理接口
if (mCallback.handleMessage(msg)) {
//执行接口处理程序,如果返回true,表示处理完成,不再往下执行
return;
}
}
handleMessage(msg); //调用Handler初始化时重写处理方法。
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
综上,Loop不断遍历消息队列,循环取出消息进行处理需要通过两个嵌套的死循环实现,外层等待待处理的消息返回,发送给Handler进行处理。内层遍历消息队列,没有消息返回时进行阻塞等待,直到有消息返回才会结束循环。
Handler是d如何保证在对应的线程处理消息的?
这和Looper的创建有关,Looper在指定的线程创建,其绑定的消息队列也和Looper在同一个线程。发送消息时可能在不同的线程,但消息最终都会存放进消息队列。Looper在其线程中遍历消息,获得待处理的消息后也是在指定线程中对消息进行分发,调用Handler.dispatchMessage(@NonNull Message msg)方法。该方法运行在指定线程中,对消息进行处理。