Looper主循环
本文承接Looper线程绑定。前文解决了“哪个线程拥有哪个 Looper”;本文解决“这个线程如何一直工作”。阅读主线是 Looper.loop() → loopOnce() → MessageQueue.next() → msg.target.dispatchMessage(msg),并沿着正常返回、队列退出、回调抛异常三条路径观察状态怎样收束。
本文不把 loop() 简化成一个无限 while。真正决定行为的是:next() 何时阻塞、何时返回 null,Looper 在分发前后保存哪些上下文,以及 Message 为什么只在正常路径进入 recycleUnchecked()。
1. 循环入口
源码文件:frameworks/base/core/java/android/os/Looper.java
相关函数:loop()、loopOnce()
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
if (me.mInLoop) {
Slog.w(TAG, "Loop again would have the queued messages be executed"
+ " before this one completed.");
}
me.mInLoop = true;
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
final int thresholdOverride = getThresholdOverride();
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}入口先查当前线程的 Looper,没有绑定就失败;已经在循环中再次调用只记录警告,不会创建第二个循环栈。ident 在循环开始时保存,后面每条消息完成后用于检查 Binder 身份是否被回调改变。
2. 取消息
源码文件:frameworks/base/core/java/android/os/Looper.java
相关函数:loopOnce()
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
Message msg = me.mQueue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return false;
}
// 后续执行日志、Observer、trace、分发和回收
}loopOnce() 不自己判断消息时间,也不直接操作链表;它把“等待、到期、取出、退出”交给队列。只有拿到非 null 的 Message,Looper 才进入一次分发。因而 next() 返回 null 是退出协议,不是“暂时没有消息”。
3. 队列等待
源码文件:frameworks/base/core/java/android/os/LegacyMessageQueue/MessageQueue.java
相关函数:next()
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int nextPollTimeoutMillis = 0;
for (;;) {
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message msg = mMessages;
if (msg != null && msg.target == null) {
do {
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 {
// 从链表摘下并返回到期消息
msg.markInUse();
return msg;
}
} else {
nextPollTimeoutMillis = -1;
}
if (mQuitting) {
dispose();
return null;
}
}
}
}空队列使用 -1 继续等待;队头消息尚未到期时,把距离 when 的毫秒数交给 native poll;到期后在锁内摘下 Message。同步屏障存在时,队列会跳过屏障后面的同步消息,寻找异步消息,因此“链表头部”不一定就是本次返回的消息。
4. 分发准备
源码文件:frameworks/base/core/java/android/os/Looper.java
相关函数:loopOnce()
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what);
}
final Observer observer = sObserver;
final long traceTag = me.mTraceTag;
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
Object token = null;
if (observer != null) {
token = observer.messageDispatchStarting();
}
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);日志对象和 Observer 都在分发前复制到局部变量,避免回调期间配置变化影响这一条消息。dispatchStart 用于区分“消息送达慢”和“回调执行慢”;workSourceUid 则把消息携带的工作来源设置到当前线程,后续必须恢复。
5. 执行消息
源码文件:frameworks/base/core/java/android/os/Looper.java
相关函数:loopOnce()、Handler.dispatchMessageImpl()
try {
msg.target.dispatchMessage(msg);
if (isLooperClearsThreadInterruptedEnabled()) {
Thread.interrupted();
}
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 {
if (doctor != null) {
doctor.stopMessageTimer(me.mAlarm);
}
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}Looper 只负责把 Message 交给 target,具体 Runnable、Callback 和 handleMessage() 的选择由 Handler 完成。正常返回会通知 Observer;捕获到 Exception 时通知异常回调并重新抛出,不会被 Looper 静默吞掉。无论正常还是异常,finally 都负责停止计时、恢复 WorkSource 和结束 trace。
6. 完成清理
源码文件:frameworks/base/core/java/android/os/Looper.java
相关函数:loopOnce()
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent));
}
msg.recycleUnchecked();
return true;这些语句只位于 try/finally 成功返回之后:打印完成日志、检查 Binder identity、回收 Message,然后让外层循环取下一条。回调抛 Exception 时异常直接离开 loopOnce(),不会继续执行这里的完成日志和 recycleUnchecked();这也是为什么不能把“回调返回”笼统等同为“Message 已回收”。
7. 退出路径
源码文件:frameworks/base/core/java/android/os/LegacyMessageQueue/MessageQueue.java
相关函数:quit(boolean)、next()
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
nativeWake(mPtr);
}
}退出线程设置 mQuitting 后唤醒 native poll。被唤醒的 next() 在锁内看到退出标志,释放 native 队列并返回 null;loopOnce() 再返回 false,外层 loop() 才结束。安全退出和立即退出的差异发生在队列清理阶段,Looper 本身没有另一套退出循环。
8. 异常路径
异常路径有两个容易混淆的层次:Looper 会把 Exception 通知 Observer 后重新抛出;而 MessageQueue.next() 对 IdleHandler 使用单独的 Throwable 捕获,记录问题后继续等待。这意味着一个普通消息回调的异常不会变成“下一条消息继续处理”的保证,是否继续取消息取决于更外层的线程异常处理方式。
Message.recycleUnchecked() 的清理逻辑位于成功分发后的 Looper 代码,以及队列丢弃消息的路径;不能仅凭“发送成功”推断对象已经回收到池中。
9. 动手验证
先定位三段主线:
rg -n "loopOnce|mQueue\.next|msg\.target\.dispatchMessage|recycleUnchecked" \
frameworks/base/core/java/android/os/Looper.java
rg -n "Message next\(|nativePollOnce|mQuitting|dispose\(\)" \
frameworks/base/core/java/android/os/LegacyMessageQueue/MessageQueue.java用 TestableLooper 可以把“入队”和“处理”拆开观察:先 Handler.post(),断言 Runnable 尚未执行,再调用 processAllMessages(),断言 Runnable 执行。这个输入验证的是队列处理边界,不是 native poll 的真实阻塞时长,也不覆盖异常后线程是否由宿主重启。
10. 边界说明
本文只追踪 Android 17 LegacyMessageQueue 的 Java 主循环。同步屏障的完整插入/移除、JNI 到 native Looper 的 epoll 实现、Message 对象池容量和 ANR 判定分别属于后续主题。读者可以从 MessageQueue.next() 的 nativePollOnce 继续进入 JNI,但不能把本文的 Java 返回时序直接当作 native 调度策略。
