一 前言
阅读本文的前提是你已经知道什么是Handler、Looper、ThreadLocal等基本类。源码位置可以在这里搜索。
二 源码分析
enqueueMessage方法分析
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
MessageQueue内部组织Message的数据结构其实是个单链表,按照Message的when字段升序排列,我们主要看下是如何把Message挂到链表上的。
Message可分为2种,一种是同步消息一种是异步消息,可通过方法isAsynchronous来判断,两种消息的区别是,当消息链表中的头部是一个屏障的时候,这个屏障后面的同步消息都无法处理,但是异步消息不受影响。
我们正常使用当中的时候绝大多数时候都是同步消息。但是添加屏障的接口是私有API,也就说,除非反射,否则开发者是无法调用这个API的。
我们现在开始分析代码,相对重要的代码是下面这几行
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
如果当前链表为null或者当前消息的触发时间(when)小于链表头的触发时间,那么直接把这个消息加到链表的头部,next指向原头部,如果当前next函数是阻塞的,那么要唤醒next函数。
如果当前链表并且msg的触发时间要比链表头部msg触发时间要大,那么就遍历这个链表,找到它应该在的位置(按when升序)。
这里面有个唤醒next 方法的策略,就是这个needWake变量,如果当前头部的msg是个屏障消息,并且next函数是阻塞的,并且msg是这个链表中触发时间最早的,那么需要唤醒next函数。我们来分析下这几个条件:
如果next没阻塞,那么无论msg是什么,我们都不需要唤醒next。
如果next阻塞,但是链表头部是非屏障消息,说明头部还不到时间执行,到了时间next会自动唤醒,所以手动不需要唤醒。
如果next 阻塞,头部是屏障消息,如果msg不是触发最早的异步消息,那么说明最早的异步消也在等待处理,next函数在指定时间差内会自动唤醒,此时不需要手动唤醒。如果msg是最早的异步消息,那msg后面有可能有异步消息,也有可能没有异步消息,在有异步消息的情况,要重新唤醒next,为了及时能处理当前msg,在没有的情况下也要唤醒next,因为此时next是永久阻塞,阻塞参数为-1。
我们分析下next函数
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
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;//链表的头部
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会阻塞一个指定时间段
// 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 {//直接返回给Looper
// 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 {//如果没找到消息,那么next可能会陷入阻塞
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// 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) {//如果没空闲任务做,那么next就会陷入阻塞,nextPollTimeoutMillis=-1的话,就永远阻塞。
// 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陷入永久阻塞,否则的话,直接处理头部消息,如果头部消息还不到时间处理,那么next会被阻塞(when-now)这么久,否则的话,直接返回给Looper