Handler构造
本文面向已经读过 消息机制总览,理解 Looper 绑定线程、MessageQueue 保存消息的读者。本文不重复 post() 入队和 native 唤醒,而是回答一个更窄的问题:创建 Handler 时,哪个构造函数决定它服务哪个线程,Callback 和 async 如何进入实例状态,以及为什么默认构造在 Android 17 被标记为过时。
读完后,你应能从任意 new Handler(...) 或 Handler.createAsync(...) 调用反查到最终构造函数,判断消息会在哪个线程消费,解释一个无 Looper 线程为何直接构造失败,并区分“空 Callback”与“没有 Callback”这两种不同的正常状态。
1. 构造分流
Handler 的公开构造器不是多套实现,而是两条汇聚路径:隐式 Looper 路径读取 Looper.myLooper(),显式 Looper 路径直接使用调用方传入的 Looper;两条路径最后都写入 mLooper、mQueue、mCallback 和 mAsynchronous。
源码文件:frameworks/base/core/java/android/os/Handler.java
相关构造函数:Handler()、Handler(Callback)、Handler(Looper)、Handler(Looper, Callback)、Handler(Callback, boolean)、Handler(Looper, Callback, boolean, boolean)
@Deprecated
public Handler() {
this(null, false);
}
@Deprecated
public Handler(@Nullable Callback callback) {
this(callback, false);
}
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);
}这段分流产生两个直接结论。第一,new Handler() 的“默认线程”不是某个全局线程,而是构造发生时当前线程的 Looper。第二,显式 Looper 构造不会把消息切换到构造线程;它把 Handler 绑定到传入 Looper 所属的线程。
2. 隐式绑定
隐式构造的核心不在重载本身,而在 Handler(Callback, boolean):它读取当前线程的 Looper,没有 Looper 就抛出异常;有 Looper 才把同一个 Looper 的队列保存到 mQueue。
源码文件:frameworks/base/core/java/android/os/Handler.java
相关函数:Handler(Callback, boolean)
public Handler(@Nullable Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass())
&& (klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: "
+ klass.getCanonicalName());
}
}
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;
mIsShared = false;
}mLooper 和 mQueue 同时保存不是重复建队列:mLooper 用于判断当前线程、查询 Looper 属性和退出关系,mQueue 是发送/移除消息的热路径引用。构造器不会创建第二个队列,也不会启动 Looper.loop()。
默认构造被标记为 @Deprecated,源码注释给出的风险是线程关联隐式、线程没有 Looper 时崩溃,以及 Handler 绑定到调用者没有预期的线程。这里的过时标记不是“Handler 不可用”,而是要求调用方显式选择 Looper 或改用 Executor。
3. 显式绑定
显式构造走四参数构造。它不调用 Looper.myLooper(),因此可以在 A 线程创建一个绑定 B 线程 Looper 的 Handler;发送线程和消费线程由此分离。
源码文件:frameworks/base/core/java/android/os/Handler.java
相关函数:Handler(Looper, Callback, boolean)、Handler(Looper, Callback, boolean, boolean)
@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
this(looper, callback, async, /* shared= */ false);
}
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async,
boolean shared) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
mIsShared = shared;
}这个构造函数的源码没有显式 null 检查;公开参数带 @NonNull 约束,调用 null Looper 会在读取 looper.mQueue 时失败。读调用方时要区分 Java 注解约束、运行时异常和构造器自身的业务分支,不能把 @NonNull 误解成自动修复。
4. 回调保存
Callback 是 Handler 级别的消息消费者。它不是 Message.callback:后者由 post(Runnable) 写入,前者在 Handler 构造时保存。后续 dispatchMessageImpl() 会按 Message.callback、Handler mCallback、handleMessage() 的顺序选择路径。
源码文件:frameworks/base/core/java/android/os/Handler.java
相关类型与字段:Handler.Callback、mCallback、dispatchMessageImpl()
public interface Callback {
boolean handleMessage(@NonNull Message msg);
}
public void dispatchMessageImpl(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}因此传入 new Handler(looper, callback) 并不会让每个 Runnable 都经过这个 Callback;Runnable 对应的 Message 有自己的 callback,会优先执行。只有没有 Runnable callback 的普通 Message,才会进入 Handler 级 Callback,再按返回值决定是否继续到 handleMessage()。
5. 异步标记
Handler 的 async 是实例策略,不是“让 Handler 在另一个线程运行”。它的作用是在入队时把发送的 Message 标记为 asynchronous;队列遇到同步屏障时,异步消息可以被选中。
源码文件:frameworks/base/core/java/android/os/Handler.java
相关函数:createAsync()、Handler(Looper, Callback, boolean, boolean)、enqueueMessage()
public static Handler createAsync(@NonNull Looper looper) {
if (looper == null) throw new NullPointerException("looper must not be null");
return new Handler(looper, null, true);
}
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);
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}createAsync() 的显式 null 检查和普通构造的注解约束不同:它在工厂入口直接抛出 NullPointerException,随后才进入共同构造器。异步保证的是该 Handler 自己发送的消息之间仍按队列规则排序,不保证它与其他 Handler 的同步消息保持全局顺序。
6. 特殊实例
同一个源码文件还包含 shared Handler 和 sentinel Handler 的内部入口。它们不是普通业务构造器,不能用来推导所有 Handler 都允许 null Looper。
源码文件:frameworks/base/core/java/android/os/Handler.java
相关函数:Handler(Looper, Callback, boolean, boolean)、createSentinelHandler()、getMain()
private Handler(Looper looper, MessageQueue queue, Callback callback) {
mLooper = looper;
mQueue = queue;
mCallback = callback;
mAsynchronous = false;
mIsShared = false;
}
static Handler createSentinelHandler() {
return new Handler(null, null, null);
}
public static Handler getMain() {
if (MAIN_THREAD_HANDLER == null) {
MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper());
}
return MAIN_THREAD_HANDLER;
}sentinel Handler 被设计为内部标记对象,允许字段为 null;普通发送方法遇到 mQueue == null 会返回失败或记录警告。getMain() 则是延迟创建的主线程 Handler,使用的是系统已经准备好的 main Looper,不等于每次调用都新建 Handler。
7. 失败边界
| 场景 | 构造入口 | 结果 | 后续状态 |
|---|---|---|---|
| 当前线程没有 Looper | Handler()、Handler(Callback) | 抛 RuntimeException | 不产生 Handler |
| 显式 Looper 为 null | Handler(Looper, ...) | 访问 looper.mQueue 时失败 | 不产生有效绑定 |
| async 工厂 Looper 为 null | createAsync(Looper) | 立即抛 NullPointerException | 不进入共同构造器 |
| async 工厂 Callback 为 null | createAsync(Looper, Callback) | 立即抛 NullPointerException | 不进入共同构造器 |
| sentinel 无队列 | 内部 createSentinelHandler() | 可创建特殊实例 | 不能正常入队 |
| 已绑定 Looper 后退出 | 任意正常 Handler | 构造不失败 | 后续发送可能返回 false |
失败 owner 很明确:没有 Looper 是 Handler 构造阶段的线程前置条件;队列退出是发送阶段的生命周期状态;Runnable 抛异常则是分发阶段的问题,不能通过改变 Handler 构造参数解决。
8. 源码复现
在 Android 源码 checkout 中执行:
rg -n "public Handler\(|Handler\(@|mLooper =|mQueue =|mCallback =|mAsynchronous =" \
frameworks/base/core/java/android/os/Handler.java
rg -n "createAsync|setAsynchronous|dispatchMessageImpl" \
frameworks/base/core/java/android/os/Handler.java
rg -n "prepare\(|ThreadLocal|new Looper" \
frameworks/base/core/java/android/os/Looper.java可以做两个只读实验。第一,在一个没有调用 Looper.prepare() 的线程执行 new Handler(),记录异常类型和消息;再在同一线程 Looper.prepare() 后创建,比较对象是否绑定当前线程。第二,使用同一个 Looper 创建普通 Handler 和 createAsync() Handler,分别发送消息并在队列存在同步屏障的环境中观察可执行性;这个实验只验证 Handler 设置 asynchronous 标记,不替代 MessageQueue 同步屏障专题。
9. 边界说明
本文解释 Handler 的实例构造和状态来源,没有展开 Handler.post() 的完整入队路径、同步屏障的链表搜索、HandlerThread 的线程启动、主线程 ActivityThread.H 的业务消息类型或 Handler 泄漏的完整生命周期。继续阅读时,应把本文得到的 mLooper、mQueue、mCallback 和 mAsynchronous 带入相应专题,而不是把构造器本身当成消息执行器。
