Handler/Looper 消息机制
问题
Android Handler/Looper 消息机制的工作原理是什么?
答案
核心组件
四个核心角色:
| 组件 | 职责 |
|---|---|
| Handler | 发送消息和处理消息的接口 |
| Message | 消息载体,包含 what、obj、data 等 |
| MessageQueue | 按时间排序的消息队列(单链表) |
| Looper | 无限循环从 MessageQueue 取出消息分发给 Handler |
Handler 工作流程
Looper.loop() 源码分析
// Looper.loop() 核心逻辑(简化)
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) { // 无限循环
// 1. 从 MessageQueue 取消息(可能阻塞)
Message msg = queue.next();
if (msg == null) {
return; // 收到 quit 消息时返回 null
}
// 2. 分发消息到对应的 Handler
msg.target.dispatchMessage(msg);
// 3. 回收消息对象
msg.recycleUnchecked();
}
}
MessageQueue.next() 在没有消息时通过 nativePollOnce() 进入 epoll 休眠,不会消耗 CPU。当有新消息时通过 nativeWake() 唤醒。ANR 是因为某条消息处理时间过长,而不是因为 Looper 循环本身。
MessageQueue 核心机制
消息排序:Messages 按 msg.when(执行时间)排序存储在单链表中。
// MessageQueue.enqueueMessage() 简化
boolean enqueueMessage(Message msg, long when) {
msg.when = when;
Message p = mMessages; // 链表头
if (p == null || when == 0 || when < p.when) {
// 插入链表头部
msg.next = p;
mMessages = msg;
} else {
// 按时间顺序找到插入位置
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) break;
}
msg.next = p;
prev.next = msg;
}
// 唤醒等待
nativeWake(mPtr);
return true;
}
同步屏障(Sync Barrier):
// 同步屏障让异步消息优先处理
// View 绘制使用异步消息 + 同步屏障保证优先级
// postSyncBarrier() → 插入 target 为 null 的 Message
// removeSyncBarrier() → 移除屏障
屏障期间,普通同步消息被"阻拦",只有标记为异步的消息(如 ViewRootImpl 的绘制消息 MSG_DO_TRAVERSAL)可以被取出执行。
Handler 常见用法
// 1. 使用 Handler + Looper.getMainLooper()(推荐)
val handler = Handler(Looper.getMainLooper()) { msg ->
when (msg.what) {
MSG_UPDATE_UI -> updateUI(msg.obj as String)
MSG_LOAD_DONE -> showResult()
}
true
}
// 子线程发送消息
thread {
val data = fetchData()
handler.obtainMessage(MSG_UPDATE_UI, data).sendToTarget()
}
// 2. 延迟执行
handler.postDelayed({
doSomething()
}, 3000)
// 3. 在子线程创建 Handler(需要 Looper)
val handlerThread = HandlerThread("background")
handlerThread.start()
val bgHandler = Handler(handlerThread.looper) { msg ->
// 在 background 线程处理
true
}
Handler 内存泄漏
// ❌ 内部类 Handler 持有外部 Activity 引用
class MyActivity : AppCompatActivity() {
val handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
// 隐式持有 MyActivity.this
textView.text = msg.obj.toString()
}
}
}
// ✅ 使用弱引用
class MyActivity : AppCompatActivity() {
private class SafeHandler(
activity: MyActivity
) : Handler(Looper.getMainLooper()) {
private val ref = WeakReference(activity)
override fun handleMessage(msg: Message) {
ref.get()?.handleMsg(msg)
}
}
private val handler = SafeHandler(this)
override fun onDestroy() {
super.onDestroy()
handler.removeCallbacksAndMessages(null) // 清除所有消息
}
}
现代 Android 开发中,lifecycleScope.launch 和 withContext 可以替代大部分 Handler 的使用场景,并且自动管理生命周期,避免泄漏。
常见面试问题
Q1: 一个线程可以有几个 Looper 和几个 Handler?
答案:
一个线程只能有一个 Looper(ThreadLocal 保证),但可以有多个 Handler。所有 Handler 发送的 Message 都进入同一个 MessageQueue,Looper 通过 msg.target(即发送该消息的 Handler)将消息分发回对应的 Handler。
Q2: Handler.postDelayed 的延迟消息是如何实现的?
答案:
postDelayed(runnable, delay) 将 SystemClock.uptimeMillis() + delay 作为消息的 when 字段,插入 MessageQueue。MessageQueue.next() 取消息时,如果队头消息的 when 大于当前时间,调用 nativePollOnce(timeout) 休眠到该时间点再唤醒。因此延迟不是通过 Thread.sleep 而是通过 epoll 超时机制实现的。
Q3: 主线程的 Looper 是何时创建的?
答案:
在 ActivityThread.main() 方法中,应用启动时调用 Looper.prepareMainLooper() 创建主线程 Looper,然后调用 Looper.loop() 进入消息循环。整个 Activity 生命周期回调(onCreate、onResume 等)都是通过主线程 Handler 接收 AMS 发送的消息来触发的。
Q4: IdleHandler 是什么?有什么用途?
答案:
IdleHandler 是 MessageQueue 提供的回调接口,在消息队列空闲时执行:
Looper.myQueue().addIdleHandler {
// 消息队列空闲时执行
doLowPriorityWork()
false // 返回 false 执行一次,true 持续执行
}
适用于延迟初始化、GC、预加载等不紧急的任务。