Skip to content

Handler 调度边界

以 Android Handler/Executor 真实源码为基线,比较 Handler、RxJava Scheduler 与 Kotlin Dispatcher 的调度、取消、错误和生命周期边界。

基于android-17.0.0_r1
AndroidHandlerRxJavaKotlin协程源码阅读

Handler 调度边界 ​

Handler、RxJava Scheduler 和 Kotlin Coroutine Dispatcher 都能表达“把工作安排到另一个执行 上下文”,但它们不是同一层抽象。Android 17 的 Handler 真实拥有 Looper、MessageQueue、 Message callback 和队列清理;HandlerExecutor 只是把这个队列适配成 Executor。RxJava 和 Kotlin 协程的 Scheduler/Dispatcher、Disposable/Job、operator/suspend 语义来自外部依赖, 不属于 AOSP 固定源码树。

本文面向已经读过 HandlerExecutor 提交语义、 HandlerThread 生命周期、消息队列性能代价 和 AsyncTask 任务生命周期 的读者。文章以 Android 17 的 Handler/Executor/HandlerThread 源码为可追溯基线,RxJava 与 Kotlin 协程只比较 公开抽象契约,不伪造它们的第三方源码块。重点回答:调度 owner 是谁、任务何时生效、取消覆盖 哪一段、异常交给谁、生命周期是否自动关联。

读完后,读者应能把一个“切到主线程”的需求拆成队列、执行器、流订阅或协程上下文四个层次, 判断应该保留 Handler 的精确消息控制,还是需要更高层的取消/组合语义,并知道哪些性能和 生命周期结论必须回到具体依赖版本验证。

1. 证据边界 ​

源码文件:

  • frameworks/base/core/java/android/os/Handler.java
  • frameworks/base/core/java/android/os/HandlerExecutor.java
  • frameworks/base/core/java/android/os/HandlerThread.java

Android 17 固定源码能直接证明 Handler 的 Looper/MessageQueue 行为,以及 HandlerExecutor 如何 把 Handler.post() 转成 Executor.execute()。本仓库不包含 RxJava、RxAndroid、kotlinx.coroutines 或 AndroidX 的源码,因此下面不使用第三方实现细节作为 AOSP 事实。

java
public class HandlerExecutor implements Executor {
    private final Handler mHandler;

    public HandlerExecutor(@NonNull Handler handler) {
        mHandler = Preconditions.checkNotNull(handler);
    }

    @Override
    public void execute(Runnable command) {
        if (!mHandler.post(command)) {
            throw new RejectedExecutionException(mHandler + " is shutting down");
        }
    }
}

这段源码是本文比较的锚点:Android 的 Executor 适配器不拥有线程、不拥有 Future,成功只 表示 Handler 接受了 Runnable。任何 RxJava/协程的更强保证,都必须由各自依赖版本的实现和 文档另行证明。

2. 调度对象 ​

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

Handler 保存 Looper、MessageQueue、Callback 和异步标志;Message 保存一次投递的 target/callback/when。调度的最小单位是 Message 或 Runnable。

java
final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
final boolean mAsynchronous;

从抽象层看,四者的 owner 不同:

抽象主要调度对象执行上下文 owner结果模型
HandlerMessage/RunnableLooper + MessageQueueboolean 入队结果
HandlerExecutorRunnable被包装的 Handler拒绝异常,无 Future
RxJava SchedulerDisposable task/流通知Scheduler/WorkeronNext/onError/onComplete + Disposable
Kotlin DispatcherRunnable/continuationCoroutineContext/Jobsuspend 结果 + Job 状态

后两行是公开抽象层比较,不宣称其内部一定使用某种线程池或 Handler。Android 端具体实现应 按依赖版本源码核对。

3. 主线程切换 ​

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

Handler 切主线程的路径是显式的:构造时绑定 Looper.getMainLooper(),调用 post() 将 Runnable 放入该 Looper 的 MessageQueue。

java
Handler mainHandler = new Handler(Looper.getMainLooper());
mainHandler.post(() -> updateUi());

这段代码不隐藏“何时执行”:Message 必须先入队、等待 when、被 MessageQueue.next() 取出, 再由 Looper 调 Handler.dispatchMessage()。RxJava 的 observeOn(mainScheduler) 和协程的 withContext(Main) 对调用者隐藏了类似的调度步骤,但它们是否立即执行、是否跳过 dispatch、 如何处理取消,必须看各自调度器契约。

4. 非主线程切换 ​

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

HandlerThread 提供一条新 Looper;new Handler(worker.getLooper()) 才会把任务放到 worker, 而 new Handler() 在主线程 callback 中仍然绑定主 Looper。

java
HandlerThread worker = new HandlerThread("worker");
worker.start();
Handler workerHandler = new Handler(worker.getLooper());
workerHandler.post(() -> doBlockingWork());

这个显式 Looper 是 Handler 的优势也是责任:读者能直接找到线程 owner,但必须自己管理 启动、停止、异常和资源。Scheduler/Dispatcher 通常把线程池或上下文选择封装在更高层,代价 是需要理解其调度器和作用域配置。

5. 延迟语义 ​

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

Handler 的延迟 API 使用 SystemClock.uptimeMillis() 计算绝对 when。延迟到期只表示消息 具备出队资格;当前 Looper 忙碌或设备深度睡眠都会改变实际回调时间。

java
public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) delayMillis = 0;
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
需求Handler 需要显式处理高层抽象通常表达
延迟一次postDelayed + Runnable/tokentimer/延迟 operator 或 delay
重复任务自己重新 post 并处理停止interval/循环协程
取消removeCallbacks/token/业务标志Disposable/Job cancellation
现实时间唤醒Handler 不提供仍需 AlarmManager 等系统 API

“高层”不等于“跨深度睡眠或进程死亡仍可靠”;这些语义不应从抽象名称推断。

6. 取消范围 ​

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

Handler 的取消 API 只覆盖仍在 MessageQueue 中、且匹配 Handler/Runnable/token 的消息;已经 由 next() 摘下或正在运行的 Runnable 无法被 removeCallbacks 撤回。

java
public final void removeCallbacks(@NonNull Runnable r, @Nullable Object token) {
    mQueue.removeMessages(this, r, token);
}

public final void removeCallbacksAndMessages(@Nullable Object token) {
    mQueue.removeCallbacksAndMessages(this, token);
}

对应比较:

取消问题HandlerRxJavaKotlin 协程
队列中任务remove APIDisposable 通常可移除订阅任务Job cancellation
已开始工作业务标志/中断合作上游是否响应 Disposablesuspend/协作式 cancellation
结果晚到自己检查 generation/tokenstream/operator 规则Job 状态与结构化作用域

后两列只描述使用者可见的抽象,不代表所有 operator、Scheduler 或 Dispatcher 都具有相同的 抢占能力;阻塞 I/O 是否可取消仍由底层 API 决定。

7. 错误模型 ​

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

Handler dispatch 中的未捕获 Exception 由 Looper 重新抛出;HandlerExecutor 没有 Future 或 错误回调,提交者不会自动收到 Runnable 异常。

java
try {
    msg.target.dispatchMessage(msg);
} catch (Exception exception) {
    throw exception;
}

对比时应看错误 ownership:

抽象错误默认去向
Handler/RunnableLooper 线程异常处理;业务可在 Runnable 内捕获
HandlerExecutor仍是 Handler/Looper 异常路径
RxJava错误进入流的 onError 协议;未处理错误由库规则处理
Kotlin 协程异常沿 Job/CoroutineExceptionHandler/调用者结构传播

RxJava/协程两行不是 AOSP 源码结论;实际行为取决于版本、operator、SupervisorJob、异常处理 器和调用方式。不能把 Handler 的异常语义直接映射成 onError 或 Job cancellation。

8. 生命周期 owner ​

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

Handler 不感知 Activity/Service 生命周期;HandlerThread owner 必须调用 quit/quitSafely, 业务还要移除消息、关闭外部资源和处理当前任务。

java
@Override
public void onDestroy() {
    mHandler.removeCallbacksAndMessages(null);
    mWorker.quitSafely();
    super.onDestroy();
}

生命周期比较不能只写“协程自动取消”:需要明确 scope owner;RxJava 也需要持有并 dispose Disposable。三种模型都需要回答“谁持有任务,谁取消,取消后结果是否还能到达”。

9. 组合能力 ​

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

HandlerExecutor 的组合能力只到 Executor 接口:可以把 Handler 传给一个接收 Executor 的 framework API,但没有 map/merge/retry/Future 等更高层操作。

java
Executor mainExecutor = new HandlerExecutor(
        new Handler(Looper.getMainLooper()));
mainExecutor.execute(() -> updateUi());
组合需求Handler 常见实现RxJava 常见抽象Kotlin 协程常见抽象
多源合并自己维护计数/状态merge/zip 等 operatorasync/await + coroutineScope
重试自己保存次数并 postDelayedretry operatortry/catch + delay
背压自己限流/丢弃Flowable/背压协议Channel/Flow buffer
结果链callback/messageObservable/Singlesuspend 返回值

这里的后两列是概念映射,不是 AOSP 中存在的代码;写具体实现时必须回到相应库版本。

10. 主线程公平 ​

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

Handler 消息按 when 排序,同一 Handler 的消息有队列顺序;postAtFrontOfQueue() 会把消息 插到队头并可能造成饥饿。异步消息可以穿过同步屏障,但不能抢占当前 callback。

java
if (p == null || when == 0 || when < p.when) {
    msg.next = p;
    mMessages = msg;
}

高层 Scheduler/Dispatcher 的公平性不能从 Handler 队列直接推断。RxJava 的 Scheduler、协程 Dispatcher 是否共享线程池、是否串行、是否立即执行,必须由具体实现和配置决定。

11. 调试证据 ​

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

Handler 的调试入口包括 Printer、slow delivery/dispatch log、Observer 和 Looper dump;这些 能观察 AOSP MessageQueue 的真实边界。高层抽象还可能有自己的日志、operator assembly 或 coroutine debug,但它们不能替代 Handler 的队列证据。

java
looper.setMessageLogging(printer);
looper.setSlowLogThresholdMs(dispatchThreshold, deliveryThreshold);
Looper.setObserver(observer); // hidden framework API

诊断“主线程没有更新”时,先看消息是否进入主 Looper,再看高层订阅/Job 是否仍活跃;否则 “observeOn/main”或“Dispatchers.Main”只说明目标抽象,不说明 Handler Message 已成功入队。

12. 版本与依赖 ​

AOSP 固定 tag 能证明 Handler、Looper、MessageQueue 和 HandlerExecutor;不能证明当前项目使用 的 RxJava/RxAndroid/kotlinx.coroutines 版本内部是否包装 Handler、是否使用 async Message、 是否在同一线程立即执行。比较文档至少应记录:

text
Android: android-17.0.0_r1
RxJava/RxAndroid: 具体依赖版本和对应源码/文档
Kotlin/kotlinx.coroutines: 具体版本和对应源码/文档

没有外部依赖版本时,本文只给出契约级比较,不给出内部类名、性能数字或“底层就是 Handler” 的结论。

13. 验证输入 ​

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

第一组验证四种调度都只改变执行上下文:Handler、HandlerExecutor、RxJava main scheduler、 Coroutine Main 分别投递一个记录线程名的任务;断言目标线程符合各自配置,不比较未经控制的 实现开销。

第二组验证取消窗口:任务在入队、已出队、已开始三个时点分别取消,记录是否执行、是否收到 结果/错误。Handler 侧可直接用受控 Looper;RxJava/协程侧必须使用固定依赖版本的测试调度器。

第三组验证异常和生命周期:让任务抛异常,在各模型记录错误去向;销毁 owner 后检查 pending 任务和迟到结果。该实验只能比较可观察契约,不能用一个库的默认配置代表所有项目。

14. 复查命令 ​

源码文件:

  • frameworks/base/core/java/android/os/Handler.java
  • frameworks/base/core/java/android/os/HandlerExecutor.java
  • frameworks/base/core/java/android/os/HandlerThread.java
  • frameworks/base/core/java/android/os/LegacyMessageQueue/MessageQueue.java
bash
rg -n "class HandlerExecutor|execute\(|RejectedExecutionException" \
  frameworks/base/core/java/android/os/HandlerExecutor.java

rg -n "postDelayed|removeCallbacks|removeCallbacksAndMessages|mQueue" \
  frameworks/base/core/java/android/os/Handler.java

rg -n "run\(|getLooper|quitSafely|Looper\.loop" \
  frameworks/base/core/java/android/os/HandlerThread.java

rg -n "enqueueMessage|mMessages|mBlocked|nativeWake" \
  frameworks/base/core/java/android/os/LegacyMessageQueue/MessageQueue.java

15. 选择边界 ​

Handler 适合需要直接控制 Android Looper、Message、屏障、精确取消和 framework API 互操作的 场景。RxJava 适合已有响应式流和 operator 组合的项目;Kotlin 协程适合需要 suspend、结构化 作用域和协作式取消的 Kotlin 项目——这些是抽象层选择,不是 AOSP 源码证明的性能排名。

最稳妥的比较方式是先写清任务契约:目标线程、延迟基准、取消时点、异常去向、结果所有权和 生命周期 owner。然后回到 Handler 或具体外部依赖源码验证每一项,而不是根据 API 名称推断 “谁更快”“谁自动清理”或“谁底层一定使用 Handler”。