Handler消息机制是Android系统中实现线程间通信,特别是主线程与子线程之间通信的核心技术。其本质是一套基于消息队列 的事件驱动模型,主要由Handler、Looper、MessageQueue和Message四个组件协作完成。下面从组件职责、工作流程、源码关键点以及常见用法等方面详细阐述。
一、核心组件及其职责
| 组件 | 作用 |
|---|---|
| Message | 消息载体,携带数据(what、arg1、arg2、obj)及目标Handler的引用。 |
| MessageQueue | 消息队列,以单链表结构存放Message,按消息的执行时间(when) 排序,提供enqueueMessage()入队和next()阻塞取消息的能力。 |
| Looper | 消息循环器,为线程绑定一个MessageQueue,不断从队列中取出消息并分发给对应的Handler处理。每个线程最多只有一个Looper。 |
| Handler | 消息发送与处理器。发送消息(sendMessage、post等)将Message塞入目标线程的MessageQueue,并实现handleMessage()回调以处理消息。 |
二、宏观工作流程
- Looper准备 :目标线程(通常是主线程)已通过
Looper.prepare()创建了Looper和MessageQueue,并调用Looper.loop()开启无限循环。 - Handler创建 :在目标线程中创建
Handler,它内部会持有该线程的Looper和MessageQueue引用。 - 消息发送 :任意线程通过该
Handler的sendMessage()或post()系列方法发送消息。Handler将消息的target设为自身,并调用MessageQueue.enqueueMessage()将消息入队。 - 消息排队 :
MessageQueue按照消息的when(触发时间)将消息插入到合适位置,若队列头有新消息且需要立即处理,则唤醒可能休眠的Looper。 - 消息循环 :
Looper.loop()循环调用MessageQueue.next()获取下一条消息(若无消息则阻塞;若遇空闲可执行IdleHandler)。 - 消息分发 :
Looper拿到消息后调用msg.target.dispatchMessage(msg),其中target就是发送消息的Handler。 - 消息处理 :
Handler.dispatchMessage()根据消息来源(post的Runnable、sendMessage的callback或默认的handleMessage())执行相应回调。
三、源码层面关键实现
1. Looper的创建与循环
java
// Looper.java
static void prepare() {
sThreadLocal.set(new Looper());
}
private Looper() {
mQueue = new MessageQueue(); // 每个Looper持有一个MessageQueue
mThread = Thread.currentThread();
}
主线程在ActivityThread.main()中已经调用了Looper.prepareMainLooper()和Looper.loop(),因此无需手动准备。
Looper.loop()核心逻辑(简化):
java
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // 可能阻塞
if (msg == null) return;
msg.target.dispatchMessage(msg);
msg.recycleUnchecked();
}
}
2. MessageQueue的入队与出队
- 入队 :
enqueueMessage()根据when字段找到插入位置,若插入到头部且当前线程正阻塞在next()中,则调用nativeWake()唤醒。 - 出队 :
next()是一个无限循环,内部先计算now,若队首消息未到触发时间,则计算等待时长并调用nativePollOnce(ptr, timeout)阻塞;否则取出消息返回。阻塞期间若被唤醒(新消息入队或超时),则重新检查。
3. Handler的消息发送与处理
发送消息链:
sendMessage(Message) → sendMessageDelayed → sendMessageAtTime → enqueueMessage(queue, msg, uptimeMillis)
enqueueMessage中会将msg.target = this,然后调用queue.enqueueMessage(msg, uptimeMillis)。
分发处理:
java
// Handler.java
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg); // 处理post(Runnable)场景
} else {
if (mCallback != null && mCallback.handleMessage(msg)) {
return;
}
handleMessage(msg); // 常规需要重写的回调
}
}
msg.callback:来自post(Runnable r),Runnable被包装成Message的callback字段。mCallback:创建Handler时可传入的Callback接口实例,优先级高于handleMessage。
四、线程切换原理
Handler消息机制之所以能实现线程切换,是因为消息的发送与处理发生在不同线程,而消息的入队与出队工作围绕同一个Looper的线程。
- 发送线程 :任意线程调用
handler.sendMessage(msg)→ 将消息置入目标Looper所在的MessageQueue(这一步通常需要同步)。 - 处理线程 :
Looper.loop()运行在目标线程(如主线程),它从MessageQueue取出消息,然后在该线程中调用handler.dispatchMessage()。因此消息最终是在创建Looper的线程中处理的。
例如,在子线程中发送消息到主线程:
- 主线程已拥有
Looper(主线程Looper)。 - 子线程通过主线程的
Handler(持有主线程Looper引用)发送消息。 - 消息入队到主线程的
MessageQueue,主线程的Looper循环到该消息并在主线程中回调。
五、关键注意点
1. 子线程使用Handler需手动准备Looper
java
new Thread(() -> {
Looper.prepare(); // 创建Looper和MessageQueue
Handler handler = new Handler();
Looper.loop(); // 开始循环
}).start();
或者直接使用HandlerThread(内部封装了Looper准备)。
2. 内存泄漏风险
非静态内部类Handler会隐式持有外部Activity引用,消息队列中未处理的消息会阻止Activity被GC。解决方案:使用静态内部类+弱引用,或在onDestroy()中移除所有消息。
3. IdleHandler
MessageQueue闲置时可执行IdleHandler,用于在空闲时做低优先级任务(如预加载、界面优化)。
4. 同步屏障与异步消息
- 通过
postSyncBarrier()插入同步屏障,此时队列会跳过所有同步消息,仅处理异步消息。 - 用于优先处理UI绘制等高优先级任务,如
ViewRootImpl的scheduleTraversals()中使用。
六、典型应用场景
| 场景 | 实现方式 |
|---|---|
| 子线程更新UI | 子线程通过主线程Handler发送消息,handleMessage中操作UI |
| 延迟任务 | sendMessageDelayed / postDelayed |
| 线程间传递数据 | 将数据放入Message的obj或Bundle中 |
| 定时周期性任务 | sendEmptyMessageAtTime 配合sendMessageAtTime递归 |
| 任务排队 | 利用Handler顺序处理消息队列中的任务(默认串行) |
七、总结流程图
text
[任意线程]
|
v
Handler.sendMessage(msg)
|
v
MessageQueue.enqueueMessage(msg) // 按时间排序入队
|
v
[Looper所在线程]
Looper.loop() 循环
|
v
MessageQueue.next() // 阻塞取消息
|
v
msg.target.dispatchMessage(msg)
|
v
Handler.handleMessage(msg) // 最终处理
Handler消息机制本质是生产者-消费者模型 :生产者(Handler发送消息)向队列添加任务,消费者(Looper+Handler)从队列取出并执行。通过绑定Looper到特定线程,实现了任务执行线程的确定性,从而保证了UI线程的单一性及线程安全。