异步 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。
@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) | null | looper == null | 子类 handleMessage 或 msg.callback |
createAsync(looper, callback) | 指定对象 | looper == null 或 callback == null | msg.callback 优先,否则 mCallback |
最后一列是 Handler 的分发规则,不是工厂自己执行回调。工厂只决定字段,真正的选择发生在 dispatchMessageImpl()。
2. 构造写入
源码文件:frameworks/base/core/java/android/os/Handler.java
两个工厂共同调用隐藏的三参数构造器,再由四参数构造器一次性写入 Handler 的核心依赖。 mIsShared 在工厂路径中固定为 false,所以异步属性和 shared Handler 是两个独立概念。
@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;
}对象关系在构造时固定:
Handler.mLooper ──> 目标 Looper
Handler.mQueue ──> Looper 的 MessageQueue
Handler.mCallback ─> 可选消息回调
Handler.mAsynchronous = truecreateAsync() 不创建 Looper、MessageQueue 或线程。传入工作线程 Looper 时,异步 Handler 也只会在那条工作线程上消费;传入主 Looper 时,仍然只有主线程负责分发。异步标志改变的是 队列选择规则,不是线程归属。
3. 工厂与构造器
源码文件:frameworks/base/core/java/android/os/Handler.java
普通显式构造器把 async 设为 false;隐藏构造器允许 framework 在兼容路径中直接传入 布尔值。工厂把这个内部能力包装成名称明确的公共入口,避免调用者在普通构造参数中阅读一个 不显眼的 true。
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 策略。
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 后续其他消息的默认同步属性。
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() 只消费消息结果。
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;
}这条路径说明三件事:
createAsync()只能让消息具备被屏障扫描选中的资格;- 消息仍需等待自己的
when到期; - 即使消息绕过屏障,也仍由同一 Looper 线程串行执行。
“异步 Handler 更快”不是源码保证。它在有屏障且存在异步消息时改变选择顺序;没有屏障时, 它仍按普通时间排序和线程负载运行。
7. Callback 分发
源码文件:frameworks/base/core/java/android/os/Handler.java
带 Callback 的工厂只是在构造时保存 mCallback。消息真正被取出后,dispatchMessageImpl() 仍按 msg.callback、mCallback、handleMessage 的顺序选择路径;异步标志不会跳过 Callback 责任链。
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 发送的消息保持全局顺序。
/**
* 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。
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,失败信息集中在工厂入口。
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。
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 时入队,否则直接调用内部实现:
@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。
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 时不受同步屏障约束”,不是把后台工作迁移到主线程。
@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()。
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;工厂没有特殊的重试或恢复分支。
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
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。这验证的是入口校验,不验证消息是否穿过屏障。
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 自动设置异步位。
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.javaframeworks/base/core/java/android/os/Message.javaframeworks/base/core/java/android/os/LegacyMessageQueue/MessageQueue.javaframeworks/base/core/java/android/view/inputmethod/IInputMethodSessionInvoker.javaframeworks/base/core/java/android/hardware/display/DisplayManagerGlobal.javaframeworks/base/core/java/android/view/contentcapture/ContentCaptureManager.java
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、屏障和退出路径;异步位只是 其中一项状态,不是整个调度生命周期。
