Skip to content

异步 Handler 工厂

从 Handler.createAsync 工厂追踪 Looper、Callback 和 mAsynchronous 的写入时机,并连接到消息入队与真实 framework 消费者。

基于android-17.0.0_r1
AndroidHandlerMessage同步屏障源码阅读

异步 Handler 工厂 ​

Handler.createAsync() 的名字容易让人联想到“异步线程”或“并行执行”。Android 17 的源码 定义了更窄的语义:工厂仍绑定一个已经存在的 Looper,只是在 Handler 创建时把 mAsynchronous 固定为 true,使之后由这个 Handler 发送的每个 Message 在统一入队点调用 setAsynchronous(true)。

本文面向已经读过 Handler构造与创建、 异步消息与 VSYNC 和 同步屏障 的读者。前两篇 已经说明异步标志如何穿过屏障;本文只回答工厂本身的源码问题:两个重载分别校验什么、 Looper 和 Callback 如何进入对象、消息的异步位在哪个时点写入,以及 framework 为什么会选择 整条 Handler 默认异步而不是逐条手动设置。本文不把“异步 Handler”解释为新线程,也不比较 AndroidX 或第三方调度器。

读完后,读者应能从 createAsync() 定位到构造器和 enqueueMessage(),判断一个 post() 是否 会设置异步位,区分 Handler 级策略与单条 Message.setAsynchronous(),并从真实消费者看出 异步 Handler 仍然受同一 Looper、队列退出和目标线程负载约束。

1. 工厂入口 ​

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

Android 17 提供两个静态工厂:一个只接收 Looper,另一个再接收一个非空 Callback。两个 重载的共同点是把第三个构造参数设为 true;差异是第二个重载把消息处理责任交给 Callback。

java
@NonNull
public static Handler createAsync(@NonNull Looper looper) {
    if (looper == null) throw new NullPointerException("looper must not be null");
    return new Handler(looper, null, true);
}

@NonNull
public static Handler createAsync(@NonNull Looper looper, @NonNull Callback callback) {
    if (looper == null) throw new NullPointerException("looper must not be null");
    if (callback == null) throw new NullPointerException("callback must not be null");
    return new Handler(looper, callback, true);
}

工厂在构造前就完成空值校验,所以 createAsync(null) 和 createAsync(looper, null) 的失败 发生在对象建立之前。成功返回只说明 Handler 已保存这些依赖,不说明 Looper 已经开始循环,也 不说明之后投递的消息一定会被处理。

重载保存的 Callback创建时失败条件后续消息处理入口
createAsync(looper)nulllooper == null子类 handleMessage 或 msg.callback
createAsync(looper, callback)指定对象looper == null 或 callback == nullmsg.callback 优先,否则 mCallback

最后一列是 Handler 的分发规则,不是工厂自己执行回调。工厂只决定字段,真正的选择发生在 dispatchMessageImpl()。

2. 构造写入 ​

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

两个工厂共同调用隐藏的三参数构造器,再由四参数构造器一次性写入 Handler 的核心依赖。 mIsShared 在工厂路径中固定为 false,所以异步属性和 shared Handler 是两个独立概念。

java
@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
    this(looper, callback, async, /* shared= */ false);
}

/** @hide */
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async,
        boolean shared) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
    mIsShared = shared;
}

对象关系在构造时固定:

text
Handler.mLooper  ──> 目标 Looper
Handler.mQueue   ──> Looper 的 MessageQueue
Handler.mCallback ─> 可选消息回调
Handler.mAsynchronous = true

createAsync() 不创建 Looper、MessageQueue 或线程。传入工作线程 Looper 时,异步 Handler 也只会在那条工作线程上消费;传入主 Looper 时,仍然只有主线程负责分发。异步标志改变的是 队列选择规则,不是线程归属。

3. 工厂与构造器 ​

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

普通显式构造器把 async 设为 false;隐藏构造器允许 framework 在兼容路径中直接传入 布尔值。工厂把这个内部能力包装成名称明确的公共入口,避免调用者在普通构造参数中阅读一个 不显眼的 true。

java
public Handler(@NonNull Looper looper) {
    this(looper, null, false);
}

public Handler(@NonNull Looper looper, @Nullable Callback callback) {
    this(looper, callback, false);
}

public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
    this(looper, callback, async, /* shared= */ false);
}

可见差异不在构造过程,而在协议命名:

写法mAsynchronous影响范围
new Handler(looper)false默认同步消息
new Handler(looper, callback)false默认同步消息,指定 Callback
Handler.createAsync(looper)true该 Handler 后续发送的所有消息
Handler.createAsync(looper, callback)true所有消息 + 指定 Callback

@NonNull 注解本身不负责工厂的运行时校验;这里的两个显式 if 才是实际抛出 NullPointerException 的代码。

4. 消息写位 ​

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

异步位不是在 createAsync() 返回时写进某个预先存在的 Message,而是在每次发送经过统一 enqueueMessage() 时写入当前消息。这个顺序保证了通过 obtainMessage() 新建的消息、 post() 包装的 Runnable 和空消息都遵守同一 Handler 策略。

java
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    msg.target = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    onBeforeEnqueue(queue, msg, uptimeMillis);
    return queue.enqueueMessage(msg, uptimeMillis);
}

入队点还写入 msg.target 和 workSourceUid。因此“异步”不是消息唯一的目标属性:消息要想 被 Looper 找回,仍必须绑定这个 Handler;工作来源 UID 也在同一次发送中采集。工厂没有绕过 这些普通 Handler 契约。

图中 E 是对象级配置,H 是每条消息的实际字段写入。两者不能合并成“工厂直接把队列 变成异步”;队列只读取消息上的异步位。

5. 单条消息 ​

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

Message.setAsynchronous() 只修改这条 Message 的 flags。普通 Handler 也能通过它发送一条 异步消息,而不改变该 Handler 后续其他消息的默认同步属性。

java
public boolean isAsynchronous() {
    return (flags & FLAG_ASYNCHRONOUS) != 0;
}

public void setAsynchronous(boolean async) {
    if (async) {
        flags |= FLAG_ASYNCHRONOUS;
    } else {
        flags &= ~FLAG_ASYNCHRONOUS;
    }
}

两种写法的所有权不同:

方式配置 owner后续影响
createAsync()Handler发送路径统一设置每条 Message
msg.setAsynchronous(true)调用方持有的 Message只影响这一条 Message

如果同一 Handler 既需要同步消息又需要异步消息,普通 Handler + 单条标记可以表达混合策略; 如果所有消息都应遵守同一异步策略,工厂则把约束集中到 Handler 的构造处。

6. 屏障读取 ​

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

同步屏障并不查看 Handler 的 mAsynchronous 字段,它只检查队列中的 Message.isAsynchronous()。 因此工厂的关键作用已经在入队时完成;next() 只消费消息结果。

java
Message msg = mMessages;
if (msg != null && msg.target == null) {
    // Stalled by a barrier. Find the next asynchronous message.
    do {
        prevMsg = msg;
        msg = msg.next;
    } while (msg != null && !msg.isAsynchronous());
}

if (msg != null && now >= msg.when) {
    // 摘除并返回异步消息
    return msg;
}

这条路径说明三件事:

  1. createAsync() 只能让消息具备被屏障扫描选中的资格;
  2. 消息仍需等待自己的 when 到期;
  3. 即使消息绕过屏障,也仍由同一 Looper 线程串行执行。

“异步 Handler 更快”不是源码保证。它在有屏障且存在异步消息时改变选择顺序;没有屏障时, 它仍按普通时间排序和线程负载运行。

7. Callback 分发 ​

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

带 Callback 的工厂只是在构造时保存 mCallback。消息真正被取出后,dispatchMessageImpl() 仍按 msg.callback、mCallback、handleMessage 的顺序选择路径;异步标志不会跳过 Callback 责任链。

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

所以 createAsync(looper, callback) 的职责是“每条消息异步 + 保存一个 Callback”,而不是把 所有 Runnable 都转成 Callback。post(r) 生成的 msg.callback 仍然优先于 Handler 构造时 保存的 mCallback。

8. 顺序范围 ​

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

工厂的 Javadoc 明确限定了顺序保证:同一个异步 Handler 的消息彼此有序,但不必与其他 Handler 发送的消息保持全局顺序。

java
/**
 * Messages sent to an async handler are guaranteed to be ordered with respect
 * to one another, but not necessarily with respect to messages from other
 * Handlers.
 */
public static Handler createAsync(@NonNull Looper looper) {
    if (looper == null) throw new NullPointerException("looper must not be null");
    return new Handler(looper, null, true);
}

这个范围来自队列的时间排序和 Handler 归属,而不是来自“异步优先级”概念。多个发送者使用 同一 Handler 时,消息会进入同一队列并按 when 处理;不同 Handler 的消息在屏障、时间相同、 线程并发入队等情况下不获得跨 Handler 的顺序承诺。

9. 输入校验 ​

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

两个工厂把空值错误分为两种:Looper 是两种重载都必需的;Callback 只在带 Callback 重载中 必需。校验发生顺序也明确:先校验 Looper,再校验 Callback。

java
public static Handler createAsync(@NonNull Looper looper, @NonNull Callback callback) {
    if (looper == null) throw new NullPointerException("looper must not be null");
    if (callback == null) throw new NullPointerException("callback must not be null");
    return new Handler(looper, callback, true);
}

这和普通 new Handler(callback, true) 的隐式 Looper 路径不同:后者先读取当前线程 Looper.myLooper(),没有 Looper 时抛“未调用 Looper.prepare()”的 RuntimeException; createAsync 则要求调用者显式给出 Looper,失败信息集中在工厂入口。

java
public Handler(@Nullable Callback callback, boolean async) {
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException("Can't create handler inside thread "
                + Thread.currentThread() + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

显式 Looper 是本文真正的构造边界:工厂不会替调用者创建线程,也不会延迟等待一个未来才 prepare 的 Looper。

10. 输入法适配 ​

源码文件:frameworks/base/core/java/android/view/inputmethod/IInputMethodSessionInvoker.java

输入法 session invoker 在本地实现不是 Binder proxy 时,创建一个共享 HandlerThread,然后 明确选择 Handler.createAsync(thread.getLooper()),用它模拟异步 one-way Binder 调用。若 对象本身是 Binder proxy,则不创建这个本地调度 Handler。

java
if (inputMethodSession != null && !Binder.isProxy(inputMethodSession)) {
    synchronized (sAsyncBinderEmulationHandlerLock) {
        if (sAsyncBinderEmulationHandler == null) {
            final HandlerThread thread = new HandlerThread("IMM.binder-emu");
            thread.start();
            // Use an async handler instead of Handler#getThreadHandler().
            sAsyncBinderEmulationHandler = Handler.createAsync(thread.getLooper());
        }
        customHandler = sAsyncBinderEmulationHandler;
    }
} else {
    customHandler = null;
}

后续方法只在 customHandler != null 时入队,否则直接调用内部实现:

java
@AnyThread
void updateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
    if (mCustomHandler == null) {
        updateCursorAnchorInfoInternal(cursorAnchorInfo);
    } else {
        mCustomHandler.post(() -> updateCursorAnchorInfoInternal(cursorAnchorInfo));
    }
}

这里的异步 Handler 不是为了产生一个新线程;线程由 HandlerThread 拥有,工厂只把该线程的 Looper 包装成“消息不受同步屏障约束”的 Handler。这个消费者也显示了异步策略与对象类型 判断、共享锁和线程生命周期共同组成完整路径。

11. 显示监听 ​

源码文件:frameworks/base/core/java/android/hardware/display/DisplayManagerGlobal.java

显示监听 API 在用户提供 Handler 后,取出其 Looper,再用 createAsync 创建一个新的异步 Handler,随后适配为 HandlerExecutor。这说明工厂经常作为“回调 Executor 的实现细节”出现, 调用方看到的接口可能已经不是 Handler。

java
public void registerDisplayListener(@NonNull DisplayListener listener,
        @Nullable Handler handler, @InternalEventFlag long internalEventFlagsMask,
        String packageName, boolean isEventFilterExplicit) {
    Looper looper = getLooperForHandler(handler);
    Handler springBoard = Handler.createAsync(looper);
    registerDisplayListener(listener, new HandlerExecutor(springBoard),
            internalEventFlagsMask, packageName, isEventFilterExplicit);
}

getLooperForHandler(handler) 决定线程来源,createAsync 决定消息属性,HandlerExecutor 只负责把 Executor 调用转回 Handler.post。三层责任不能合成“DisplayManager 自己开了异步线程”。

12. 内容捕获 ​

源码文件:frameworks/base/core/java/android/view/contentcapture/ContentCaptureManager.java

ContentCaptureManager 把主线程 UI handler 作为懒字段创建,并把后台 handler 与 UI handler 分开 保存。这里异步工厂的作用是“回到主 Looper 时不受同步屏障约束”,不是把后台工作迁移到主线程。

java
@GuardedBy("mLock")
private Handler mUiHandler;

@GuardedBy("mLock")
private Handler mContentCaptureHandler;

private Handler prepareContentCaptureHandler() {
    if (mContentCaptureHandler == null) {
        mContentCaptureHandler = BackgroundThread.getHandler();
    }
    return mContentCaptureHandler;
}

@GuardedBy("mLock")
private Handler prepareUiHandler() {
    if (mUiHandler == null) {
        mUiHandler = Handler.createAsync(Looper.getMainLooper());
    }
    return mUiHandler;
}

源码中的锁注解揭示了另一个边界:工厂本身线程安全地构造一个 Handler,并不意味着 ContentCaptureManager 的字段初始化可以脱离 mLock。对象所有权和异步消息属性是两层不同 的并发问题。

13. 工作线程路径 ​

源码文件:frameworks/base/core/java/com/android/internal/app/PlatLogoActivity.java

并非所有 createAsync 使用都在主 Looper。PlatLogo 的振动线程先启动 HandlerThread,再用 带 Callback 的异步工厂绑定 worker Looper;销毁时由 owner 调用 quit()。

java
mVibeThread = new HandlerThread("VibratorThread");
mVibeThread.start();
mVibeHandler = Handler.createAsync(mVibeThread.getLooper(), this);

public void destroy() {
    mVibeThread.quit();
}

这个例子直接否定“异步 Handler 只对主线程有意义”的泛化。即使 worker Looper 通常没有同步 屏障,异步属性仍是该回调通道的明确协议;但它不会改变 HandlerThread 的单线程串行性质, 也不会代替 destroy() 的生命周期管理。

14. 退出边界 ​

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

异步 Handler 仍使用普通 sendMessageAtTime 和 MessageQueue.enqueueMessage。队列退出后, 入队返回 false;工厂没有特殊的重试或恢复分支。

java
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

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

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

因此“异步”只影响同步屏障选择,不影响退出、回收、队列锁和消息失败语义。调用方如果需要在 线程退出时停止投递,仍应由拥有 HandlerThread 的对象先关闭提交入口,再调用 quit() 或 quitSafely()。

15. 验证输入 ​

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

本篇的第一组可执行验证是工厂入口的空值边界:分别传入空 Looper、空 Callback,断言在构造 之前抛出 NullPointerException。这验证的是入口校验,不验证消息是否穿过屏障。

java
assertThrows(NullPointerException.class,
        () -> Handler.createAsync(null));

Looper looper = Looper.getMainLooper();
assertThrows(NullPointerException.class,
        () -> Handler.createAsync(looper, null));

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

第二组验证比较 Handler 级和 Message 级属性:普通 Handler 发送的消息默认不是异步;普通 Handler 上显式调用 setAsynchronous(true) 后,只有该消息变为异步;createAsync 发送的 消息则由 enqueueMessage 自动设置异步位。

java
Handler normal = new Handler(looper);
Message one = normal.obtainMessage(1);
assertFalse(one.isAsynchronous());

one.setAsynchronous(true);
assertTrue(one.isAsynchronous());

Handler async = Handler.createAsync(looper);
Message two = async.obtainMessage(2);
async.sendMessage(two);
assertTrue(two.isAsynchronous());

这组代码应在消息仍可观察且未被 Looper 回收的测试环境中执行;它证明字段写入时机,不证明 目标线程已经执行消息。

16. 复查命令 ​

源码文件:

  • frameworks/base/core/java/android/os/Handler.java
  • frameworks/base/core/java/android/os/Message.java
  • frameworks/base/core/java/android/os/LegacyMessageQueue/MessageQueue.java
  • frameworks/base/core/java/android/view/inputmethod/IInputMethodSessionInvoker.java
  • frameworks/base/core/java/android/hardware/display/DisplayManagerGlobal.java
  • frameworks/base/core/java/android/view/contentcapture/ContentCaptureManager.java
bash
rg -n "createAsync|mAsynchronous|new Handler\(.*boolean|enqueueMessage" \
  frameworks/base/core/java/android/os/Handler.java

rg -n "setAsynchronous|isAsynchronous|FLAG_ASYNCHRONOUS" \
  frameworks/base/core/java/android/os/Message.java

rg -n "target == null|isAsynchronous|mQuitting" \
  frameworks/base/core/java/android/os/LegacyMessageQueue/MessageQueue.java

rg -n "Handler\.createAsync|mCustomHandler|sAsyncBinderEmulationHandler" \
  frameworks/base/core/java/android/view/inputmethod/IInputMethodSessionInvoker.java \
  frameworks/base/core/java/android/hardware/display/DisplayManagerGlobal.java

阅读一条具体消息时,按 createAsync → 构造器 → mAsynchronous → enqueueMessage → Message.isAsynchronous → MessageQueue.next 的顺序追踪。只看工厂返回值,无法判断消息 是否真的绕过屏障;只看消息异步位,也无法判断它属于哪个 Looper。

17. 适用边界 ​

Handler.createAsync 适合一个 Handler 的所有消息都不应受同步屏障约束,且线程归属已经由 调用者明确决定的场景。它不提供新线程、并行执行、主线程抢占、队列退出恢复或跨 Handler 全局 顺序。

如果只有一条消息需要异步,普通 Handler 加 Message.setAsynchronous(true) 能把影响范围 限制在该消息;如果同一回调通道的每条消息都有相同协议,工厂则把策略集中保存于 Handler。 无论选择哪一种,都要继续检查消息的 owner、目标 Looper、when、屏障和退出路径;异步位只是 其中一项状态,不是整个调度生命周期。