Skip to content

Handler Executor 提交边界

以 Android Handler/HandlerExecutor 与 OpenJDK ExecutorService/FutureTask 源码为基线,比较提交、结果、取消、拒绝和关闭语义。

基于android-17.0.0_r1
AndroidHandlerExecutorFutureTask源码阅读

Handler Executor 提交边界 ​

Handler 和 Java Executor 都接受 Runnable,但它们拥有的状态不同:Handler 把任务变成 Message,交给一个既有 Looper 的单线程队列;Executor 只定义提交接口,线程、队列、拒绝和 并发由实现决定;ExecutorService 再增加 Future、关闭和等待终止;FutureTask 保存结果、 异常和取消状态。

本文面向已经读过 HandlerExecutor 提交语义、 AsyncTask 任务生命周期、消息队列性能代价 和 HandlerThread 生命周期 的读者。Android 部分依据 android-17.0.0_r1 的 AOSP 源码;Java 并发部分依据 OpenJDK 21 src.zip 中的 标准库源码。本文不使用归档文章的性能数字或工厂默认值,重点回答:提交成功到底意味着 什么,结果和异常由谁拥有,取消能否停止工作,关闭如何处理等待任务,以及如何在 Handler 和 Executor 之间做准确适配。

读完后,读者应能从一次 API 调用定位队列/线程 owner,解释为什么 HandlerExecutor.execute() 没有 Future,区分 FutureTask.cancel() 与 Handler 消息移除,并根据需要选择 Handler、单线程 Executor、线程池或两者组合。

1. 接口层级 ​

源码文件:java.base/java/util/concurrent/Executor.java

OpenJDK 的 Executor 只有一个 execute(Runnable) 方法,接口文档明确说明实现可以在调用线程、 新线程或池线程执行,接口本身不承诺异步。

java
public interface Executor {
    void execute(Runnable command);
}

源码文件:java.base/java/util/concurrent/ExecutorService.java

ExecutorService 在 Executor 之上增加 submit、Future、shutdown、shutdownNow 和 awaitTermination。这些是标准库源码的能力,不是 Android HandlerExecutor 自动拥有的能力。

java
public interface ExecutorService extends Executor, AutoCloseable {
    <T> Future<T> submit(Callable<T> task);
    void shutdown();
    List<Runnable> shutdownNow();
    boolean isTerminated();
    boolean awaitTermination(long timeout, TimeUnit unit)
            throws InterruptedException;
}
类型任务 owner结果关闭能力
HandlerMessageQueue/Looper无内置 FutureLooper quit
HandlerExecutor被包装 Handler无 Future委托 Handler 队列
Executor实现自定无内置结果未定义
ExecutorServiceExecutorServiceFutureshutdown/await

2. Handler 适配 ​

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

HandlerExecutor 的实现只有一个 Handler 字段。execute() 调用 Handler.post();返回 false 时转换为 RejectedExecutionException。

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

成功只表示 Message 入队,不表示 Runnable 已开始或完成;HandlerExecutor 不创建线程、不返回 Future、不提供取消句柄、不管理 HandlerThread 生命周期。

3. Handler 提交 ​

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

Handler 的 post() 先生成带 callback 的 Message,再走 sendMessageDelayed/AtTime;目标线程 来自构造时保存的 Looper。

java
public final boolean post(@NonNull Runnable r) {
    return sendMessageDelayed(getPostMessage(r), 0);
}

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

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

入队会按 when 排序;队列退出时返回 false 并回收 Message。

java
if (mQuitting) {
    msg.recycle();
    return false;
}
msg.markInUse();
msg.when = when;
// 按 when 插入链表

4. 线程池提交 ​

源码文件:java.base/java/util/concurrent/ThreadPoolExecutor.java

OpenJDK ThreadPoolExecutor.execute() 的行为由 worker 数量、work queue 和拒绝处理器共同 决定;它不是单一的“提交到某条线程”。源码中的核心路径会尝试创建 worker、入队,再检查 线程池状态,必要时调用 RejectedExecutionHandler。

java
public void execute(Runnable command) {
    if (command == null) throw new NullPointerException();
    int c = ctl.get();
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true)) return;
        c = ctl.get();
    }
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        if (!isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    } else if (!addWorker(command, false)) {
        reject(command);
    }
}

对比 Handler:Handler 的队列 owner 固定为一个 Looper;ThreadPoolExecutor 的并发度、队列容量、 worker 创建时机和拒绝策略都由实例配置。

5. 提交结果 ​

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

HandlerExecutor 只能用异常表达队列拒绝,调用者没有任务完成对象。

java
if (!mHandler.post(command)) {
    throw new RejectedExecutionException(mHandler + " is shutting down");
}

源码文件:java.base/java/util/concurrent/ExecutorService.java

ExecutorService 的 submit(Callable) 返回 Future,Future 的 get() 会等待并报告结果、异常或 取消。

java
<T> Future<T> submit(Callable<T> task);

// Future contract: get returns result, throws ExecutionException/CancellationException,
// and may block until completion.

两者的提交语义不能互换:

结果问题HandlerExecutorExecutorService.submit
当场拒绝抛 RejectedExecutionException抛 RejectedExecutionException
已接受无返回对象返回 Future
完成确认需业务 callback/latchFuture.isDone/get
异常读取Looper 线程异常路径Future.get 抛 ExecutionException

6. Future 状态 ​

源码文件:java.base/java/util/concurrent/FutureTask.java

FutureTask 用状态值区分 NEW、NORMAL、EXCEPTIONAL、CANCELLED、INTERRUPTING 和 INTERRUPTED; get() 根据最终状态返回 outcome 或抛异常。

java
private volatile int state;
private static final int NEW = 0;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;

public V get() throws InterruptedException, ExecutionException {
    int s = state;
    if (s <= COMPLETING) s = awaitDone(false, 0L);
    return report(s);
}

Handler Message 没有等价的完成状态字段;Message 的 flags/in-use 表示队列和对象生命周期, 不是业务结果状态。

7. 取消语义 ​

源码文件:java.base/java/util/concurrent/FutureTask.java

FutureTask.cancel(mayInterruptIfRunning) 先尝试 CAS 状态;参数为 true 时尝试中断 runner, 但 OpenJDK 文档明确这是 best effort,任务是否响应由任务代码决定。

java
public boolean cancel(boolean mayInterruptIfRunning) {
    if (!(state == NEW && STATE.compareAndSet(this, NEW,
            mayInterruptIfRunning ? INTERRUPTING : CANCELLED))) {
        return false;
    }
    if (mayInterruptIfRunning) {
        Thread t = runner;
        if (t != null) t.interrupt();
        STATE.setRelease(this, INTERRUPTED);
    }
    finishCompletion();
    return true;
}

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

Handler 取消通过匹配并移除仍在队列中的 Message;已经出队的 Runnable 不会被中断。

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

因此“Future.cancel 更强”也不能绝对化:interrupt 不会强制终止不响应中断的阻塞任务;两者 都需要任务本身具备协作式停止协议,只是控制面不同。

8. 关闭语义 ​

源码文件:java.base/java/util/concurrent/ExecutorService.java

shutdown() 停止接收新任务但允许已提交任务完成;shutdownNow() 尝试停止运行任务并返回 尚未开始的 Runnable,且不等待运行任务终止。

java
void shutdown();
List<Runnable> shutdownNow();
boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException;

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

Looper quit() 会立即终止,不保证待处理消息执行;quitSafely() 只处理已经到期消息,未来 延迟消息不会执行。

java
public void quit() {
    mQueue.quit(false);
}

public void quitSafely() {
    mQueue.quit(true);
}
关闭问题HandlerThread/LooperExecutorService
拒绝新任务Handler send 返回 falseRejectedExecutionException
等待已提交任务无统一 Future/await APIawaitTermination
返回未开始任务无shutdownNow 返回 List
延迟任务quitSafely 不执行未来消息取决于队列/实现和 shutdown 语义

9. 串行模型 ​

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

一个 HandlerThread 只有一个 Looper,多个 Handler 可以共享同一队列;同一时刻只有一个消息 在该 Looper 线程执行。

java
HandlerThread thread = new HandlerThread("serial-worker");
thread.start();
Handler handler = new Handler(thread.getLooper());
handler.post(taskA);
handler.post(taskB);

源码文件:java.base/java/util/concurrent/Executor.java

Executor 接口本身甚至允许 inline executor;串行或并行不是接口结论,而是实现配置。

java
Executor inline = Runnable::run;

如果需要 Executor 的串行队列,应选择单线程实现或显式 SerialExecutor;不能因为类型名是 Executor 就假设并发,也不能因为类型名是 Handler 就假设任务一定异步到另一个线程。

10. 线程池模型 ​

源码文件:java.base/java/util/concurrent/ThreadPoolExecutor.java

ThreadPoolExecutor 的核心参数包括 corePoolSize、maximumPoolSize、workQueue、keepAliveTime 和 RejectedExecutionHandler。队列是否有界、worker 是否扩容、任务被拒绝还是排队,都由这些 参数共同决定。

java
new ThreadPoolExecutor(
        2, 4, 30, TimeUnit.SECONDS,
        new ArrayBlockingQueue<>(100),
        threadFactory,
        rejectedExecutionHandler);

Handler 的 MessageQueue 没有 FutureTask 那种 worker 扩缩容/拒绝策略配置;它的优势是 Android Looper 集成,Executor 的优势是并发和关闭控制。两者不应以单一“性能更好”排序。

11. 适配组合 ​

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

Android 17 的 HandlerThread 直接提供 getThreadExecutor(),它返回由该线程 Handler 包装的 HandlerExecutor;这条适配保留单线程 Looper 语义,同时满足只接收 Executor 的 API。

java
public Executor getThreadExecutor() {
    if (mExecutor == null) {
        mExecutor = new HandlerExecutor(getThreadHandler());
    }
    return mExecutor;
}

组合后的 Executor 仍没有 Future/await;若 API 需要结果,应在任务协议中额外使用 Future、 callback 或 CompletableFuture,不能从 getThreadExecutor() 推导结果能力。

12. 失败路径 ​

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

HandlerExecutor 的失败只有 Handler.post 返回 false 这一层;任务运行时异常仍沿 Looper dispatch 路径传播。

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

源码文件:java.base/java/util/concurrent/ThreadPoolExecutor.java

ThreadPoolExecutor 的失败可能来自 null command、池关闭、队列满和拒绝处理器;FutureTask 则 把 Callable 异常保存到 outcome,直到 get() 报告。

java
if (command == null) throw new NullPointerException();
if (!addWorker(command, false)) {
    reject(command);
}

这两种失败都不是“任务执行失败”的同义词:拒绝发生在提交阶段,Callable 异常发生在执行阶段。

13. 可追踪主线 ​

图中两条路径的关键差异是结果 owner:Handler 路径在 Looper dispatch 后没有 Future;ExecutorService 路径由 FutureTask 保存状态并支持 get/cancel。

14. 验证输入 ​

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

第一组验证提交:HandlerThread 运行时调用 HandlerExecutor.execute,断言 Runnable 在线程 TID 上执行;quit 后再次 execute,断言 RejectedExecutionException。

源码文件:java.base/java/util/concurrent/FutureTask.java

第二组验证结果:用 ExecutorService.submit Callable,分别正常返回、抛异常和 cancel,断言 Future.get 的结果/ExecutionException/CancellationException;对比 Handler 任务没有对应 Future 状态。

第三组验证关闭:HandlerThread quitSafely 与 ExecutorService.shutdown/shutdownNow 分别安排 未来任务、运行中任务和新提交,记录已开始、未开始、拒绝和终止状态。不要用单个实现的结果 概括所有 Executor。

15. 复查命令 ​

源码文件:

  • frameworks/base/core/java/android/os/HandlerExecutor.java
  • frameworks/base/core/java/android/os/HandlerThread.java
  • frameworks/base/core/java/android/os/Handler.java
  • frameworks/base/core/java/android/os/Looper.java
  • java.base/java/util/concurrent/Executor.java
  • java.base/java/util/concurrent/ExecutorService.java
  • java.base/java/util/concurrent/FutureTask.java
  • java.base/java/util/concurrent/ThreadPoolExecutor.java
bash
rg -n "class HandlerExecutor|RejectedExecutionException|mHandler.post" \
  frameworks/base/core/java/android/os/HandlerExecutor.java

rg -n "getThreadExecutor|getThreadHandler|quitSafely|Looper.loop" \
  frameworks/base/core/java/android/os/HandlerThread.java

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

16. 选择边界 ​

选择 Handler 时,核心问题是是否需要 Android Looper、Message、同步屏障、主线程和 framework 回调集成;选择 ExecutorService 时,核心问题是并发度、队列容量、Future 结果、拒绝策略和 关闭等待。HandlerExecutor 只解决接口适配,不把 Handler 变成线程池,也不把 Executor 变成 Android 生命周期组件。

比较时先写清提交结果、任务完成、异常、取消和关闭五个契约,再用 Android AOSP 与具体 JDK 版本源码验证。这样比用“Handler 轻量、Executor 并行”的一句话结论更能指导真实源码阅读和 故障定位。