Skip to content

Handler消息处理

从 Looper 取出 Message 开始,追踪 Handler 的三路分发、Observer 与 WorkSource 上下文、异常传播和消息回收边界。

基于android-17.0.0_r1
AndroidHandlerLooperMessage源码阅读

Handler消息处理 ​

本文面向已经读过 消息机制总览、Handler构造 和 Handler消息发送 的读者。前三篇已经说明 Handler 绑定哪个 Looper、消息如何进入队列;本文从 MessageQueue.next() 返回一条 Message 开始,回答它怎样在 Looper 线程中进入 Runnable、Handler.Callback 或 handleMessage(),以及执行前后的上下文由谁维护。

本文不把 dispatchMessage() 的十几行代码孤立解释为“三级回调”。真正的处理边界还包括 Looper Observer、WorkSource、慢消息计时、Binder identity、异常传播与 Message 回收。读完后,你应能判断某条消息走哪一分支,解释 Callback 返回值为什么会截断 handleMessage(),并知道回调抛异常后哪些清理仍会发生、哪些不会发生。

1. 分发入口 ​

Looper.loop() 自身只维持循环;一次消息处理发生在 loopOnce()。队列返回 null 表示正在退出,Looper 结束;返回 Message 后,Looper 读取日志、Observer、trace 和慢消息阈值,再调用 msg.target.dispatchMessage(msg)。

源码文件:frameworks/base/core/java/android/os/Looper.java

相关函数:loop()、loopOnce()

java
public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException(
                "No Looper; Looper.prepare() wasn't called on this thread.");
    }
    me.mInLoop = true;
    final long ident = Binder.clearCallingIdentity();
    final int thresholdOverride = getThresholdOverride();
    for (;;) {
        if (!loopOnce(me, ident, thresholdOverride)) {
            return;
        }
    }
}

private static boolean loopOnce(final Looper me,
        final long ident, final int thresholdOverride) {
    Message msg = me.mQueue.next();
    if (msg == null) {
        return false;
    }
    // ... prepare logging, observer, trace and timing state ...
    msg.target.dispatchMessage(msg);
    // ... finish logging, identity checks and recycle ...
    return true;
}

target 是发送阶段由 Handler 写入的引用,因此 Looper 不需要知道消息业务类型。一个队列可以混合多个 Handler 的消息,出队后每条 Message 自己指出消费者。

2. 三路选择 ​

Handler 的核心分发顺序是互斥选择,不是三层都执行。Message 自带 Runnable 时第一分支立即执行;没有 Runnable 才检查构造时保存的 Handler.Callback;Callback 不存在或返回 false 时才调用可重写的 handleMessage()。

源码文件:frameworks/base/core/java/android/os/Handler.java

相关函数:dispatchMessage()、dispatchMessageImpl()、handleCallback()

java
public void dispatchMessage(@NonNull Message msg) {
    dispatchMessageImpl(msg);
}

public void dispatchMessageImpl(@NonNull Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

private static void handleCallback(Message message) {
    message.callback.run();
}

这三条路径对应三个不同 owner:Runnable 属于单条 Message;Handler.Callback 属于一个 Handler 实例;handleMessage() 属于 Handler 子类。它们共享队列和线程,但生命周期与复用范围不同。

3. Runnable路径 ​

Runnable 路径来自 Handler.post() 对 Message.callback 的写入。即使 Handler 同时传入了 Callback 或重写了 handleMessage(),只要当前 Message 有 callback,这两层都不会执行。

源码文件:frameworks/base/core/java/android/os/Handler.java

相关函数:getPostMessage()、handleCallback()

java
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

private static void handleCallback(Message message) {
    message.callback.run();
}

Runnable 会在 Handler 绑定的 Looper 线程中运行,不是在 post 调用线程中运行。run() 没有额外 try/catch 隔离;Runnable 抛出的 Exception 会回到 Looper 的异常分支。

4. Callback路径 ​

Handler.Callback 是构造参数,它可以消费普通 Message。返回 true 表示消息已处理,Handler 立即 return;返回 false 表示继续进入 handleMessage()。

源码文件:frameworks/base/core/java/android/os/Handler.java

相关类型:Handler.Callback

java
public interface Callback {
    boolean handleMessage(@NonNull Message msg);
}

因此“Callback 用于日志、子类用于业务”的写法必须返回 false。若返回 true,子类 handleMessage() 不会执行。反过来,Message.callback 存在时 Handler.Callback 连观察机会都没有;若需要覆盖所有消息,应使用 Looper logging 或 Observer,而不是只依赖 Handler.Callback。

5. 子类路径 ​

handleMessage() 默认是空实现。子类通过覆写它消费普通 Message,通常按 what 选择业务分支。

源码文件:frameworks/base/core/java/android/os/Handler.java

相关函数:handleMessage()

java
public void handleMessage(@NonNull Message msg) {
}

空实现意味着一个没有 Callback、也没有覆写 handleMessage() 的 Handler 仍可成功收发 Message,只是普通 Message 到达后没有业务效果。队列不把“没有处理代码”视为错误。

6. 执行上下文 ​

Looper 在调用 Handler 前后维护多个线程级上下文。最关键的是 Observer token 和 WorkSource:Observer 在分发开始时产生 token,正常完成或 Exception 时必须回传一次;WorkSource 在回调期间切换到发送 Message 时保存的 UID,finally 中恢复原值。

源码文件:frameworks/base/core/java/android/os/Looper.java

相关函数:loopOnce()

java
Object token = null;
if (observer != null) {
    token = observer.messageDispatchStarting();
}
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);

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 {
    ThreadLocalWorkSource.restore(origWorkSource);
    if (traceTag != 0) {
        Trace.traceEnd(traceTag);
    }
}

LOOPER_CLEARS_THREAD_INTERRUPTED 是兼容变更控制的行为:启用时,每条消息分发后清除线程 interrupted 状态,防止一个任务污染下一条任务。本文只说明它在成功分发后的时机,不展开 Compat Change 判定;相关机制见 Compat变更。

7. 异常路径 ​

Handler 和 Looper 不会吞掉业务 Exception。loopOnce() 通知 Observer 后重新抛出;finally 仍会停止计时器、恢复 WorkSource 和结束 trace,但正常分发之后的代码不会继续执行。

源码文件:frameworks/base/core/java/android/os/Looper.java

相关函数:loopOnce()

java
} 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);
    }
}

// Only reached after normal dispatch.
msg.recycleUnchecked();

因此 Exception 路径的边界是:Observer 获得异常回调,线程上下文被恢复,异常继续离开 loopOnce();完成日志、Binder identity 校验和 Message 回收不会执行。catch 类型是 Exception,不是所有 Throwable;Error 不进入 Observer 的 dispatchingThrewException() 分支,但 finally 仍会运行。

这也是为什么不能把 Handler 当作任务隔离器。需要局部容错的业务应在 Runnable、Callback 或 handleMessage() 内建立清晰的异常策略,而不是期待 Looper 自动继续下一条消息。

8. 身份校验 ​

正常分发完成后,Looper 再次调用 Binder.clearCallingIdentity(),与进入循环时记录的 identity 比较。如果回调改变 Binder identity 却没有恢复,Looper 记录 wtf,指出目标 Handler、callback 和 what。

源码文件:frameworks/base/core/java/android/os/Looper.java

相关函数:loopOnce()

java
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
    Log.wtf(TAG, "Thread identity changed from 0x"
            + Long.toHexString(ident) + " to 0x"
            + Long.toHexString(newIdent) + " while dispatching to "
            + msg.target.getClass().getName() + " "
            + msg.callback + " what=" + msg.what);
}

这个检查只在正常返回后运行。它检测线程身份泄漏,不负责恢复业务状态、锁或 Binder 调用结果。

9. 消息回收 ​

正常处理结束后,Looper 调用 msg.recycleUnchecked()。Message 不再属于 Handler 或 MessageQueue,而是有机会回到对象池;业务代码不得在分发结束后继续持有并修改同一个 Message,随后再次使用应重新 obtain。

源码文件:frameworks/base/core/java/android/os/Looper.java

相关函数:loopOnce()

java
if (logging != null) {
    logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}

// ... Binder identity and Perfetto completion ...
msg.recycleUnchecked();
return true;

回收位于正常路径末尾,而不是 dispatch try/finally 中。回调抛 Exception 时,当前方法不会走到这里。Message 对象池的字段清理和容量将在 Message 专题单独展开。

10. 测试输入 ​

TestableLooperTest.testMessageExecuted 先 post 一个 mock Runnable,断言尚未处理队列时 run() 没被调用;执行 processAllMessages() 后再断言调用发生。它验证 post 与 dispatch 之间存在队列边界。

源码文件:frameworks/base/tests/testables/tests/src/android/testing/TestableLooperTest.java

相关测试:testMessageExecuted()、testMessageCallback()

java
Handler h = new Handler();
Runnable r = mock(Runnable.class);
h.post(r);
verify(r, never()).run();
mTestableLooper.processAllMessages();
verify(r).run();

同一测试类还通过 MessageHandler 控制测试 Looper 是否继续处理后续消息,说明测试工具可以在 Message 到达 Handler 前后插入控制点。它不直接覆盖 Looper Observer 的异常 token、Binder identity 泄漏或真实主线程崩溃行为。

11. 复现路径 ​

bash
rg -n "dispatchMessage|dispatchMessageImpl|handleCallback|handleMessage" \
  frameworks/base/core/java/android/os/Handler.java
rg -n "loopOnce|messageDispatchStarting|messageDispatched|dispatchingThrewException" \
  frameworks/base/core/java/android/os/Looper.java
rg -n "ThreadLocalWorkSource|clearCallingIdentity|recycleUnchecked" \
  frameworks/base/core/java/android/os/Looper.java
rg -n "testMessageExecuted|testMessageCallback|dispatchMessage" \
  frameworks/base/tests/testables/tests/src/android/testing/TestableLooperTest.java

给定“Message 到了但 handleMessage() 没执行”的现象,依次检查 msg.callback 是否非空、Handler.Callback 是否返回 true、消息的 target 是否是预期 Handler,以及回调是否在前一层抛异常。只看 what 无法覆盖这四种分支。

12. 边界 ​

本文覆盖 Message 出队之后的 Handler 分发与 Looper 上下文管理。没有展开 MessageQueue 的阻塞与唤醒、消息发送 API、Looper Observer 的注册者、慢消息阈值配置、Message 对象池实现或主线程 ANR 判定;这些对象需要从各自 owner 继续阅读。