Handler 消息机制 面试知识点

整理自模拟面试,覆盖 Handler / Looper / MessageQueue / Message、源码链路、内存泄漏、主线程模型


1. Handler 机制是什么

一句话:Handler 机制本质上是 Android 的线程消息循环模型,用来把任务投递到指定线程顺序执行,最常见的是把子线程结果切回主线程更新 UI。

四个核心角色

最常见用途


2. Looper / MessageQueue / Handler 三者关系

一句话Looper 负责循环取消息,MessageQueue 负责存消息,Handler 负责发消息和处理消息,三者一起构成一个线程的任务调度模型。

持有关系

工作流程

  1. Looper.prepare() 给线程创建 Looper
  2. Looper 创建时顺带创建 MessageQueue
  3. Handler 创建时绑定当前线程的 LooperMessageQueue
  4. Handler 通过 sendMessage() / post() 把消息放进 MessageQueue
  5. Looper.loop() 不断从队列中取消息
  6. 取到消息后分发给对应的 Handler

面试速记


3. 从源码看 Handler 机制主链路

1. Looper.prepare()

源码核心点:
- 通过 ThreadLocalLooper 绑定到当前线程
- 同时创建当前线程对应的 MessageQueue
- 一个线程只能调用一次,否则直接抛异常

关键源码关键词:
- ThreadLocal<Looper> sThreadLocal
- sThreadLocal.set(new Looper(...))
- Only one Looper may be created per thread

2. Handler 构造方法

源码核心点:
- Handler 创建时会调用 Looper.myLooper()
- 如果当前线程没有 Looper,会抛出经典异常
- 同时保存 mLoopermQueue

关键源码关键词:
- mLooper = Looper.myLooper()
- mQueue = mLooper.mQueue
- Can't create handler inside thread that has not called Looper.prepare()

3. 发送消息

无论是 sendMessage() 还是 post(),最终都会走到入队逻辑。

源码核心点:
- sendMessageAtTime()
- enqueueMessage()
- 入队前会执行 msg.target = this

这里的意义是:
- 消息先记住“是谁发的”
- 之后 Looper 取出消息时,才能再准确分发回这个 Handler

4. 消息入队

MessageQueue.enqueueMessage() 会按 msg.when 把消息插入到合适位置,而不是简单尾插。

这说明:
- MessageQueue 支持延迟消息
- 底层更像按时间排序的有序链表,而不是普通 FIFO 队列

5. Looper.loop()

这是消息循环的核心源码。

关键逻辑:
- for (;;) { } 死循环
- queue.next() 取下一条消息
- msg.target.dispatchMessage(msg) 分发消息

这里能说明两件事:
- 真正不断取消息的是 Looper
- 最终处理消息的是消息里记录的那个 Handler

6. dispatchMessage()

源码里消息分发优先级是:
1. msg.callback != null,先执行 Runnable
2. 否则执行 Handler.Callback
3. 最后才走 handleMessage()

所以:
- post(Runnable) 本质也是发消息
- 只是把 Runnable 塞进了 Message.callback


4. 主线程为什么天生就能用 Handler

一句话:因为系统在应用启动时已经为主线程创建好了 Looper 并启动了消息循环。

源码入口

结论


5. 为什么子线程直接创建 Handler 会报错

原因:子线程默认没有 Looper

典型异常

Can't create handler inside thread that has not called Looper.prepare()

根本原因

正确理解


6. post()sendMessage() 的区别

相同点

不同点

方法 更适合的场景 底层表现
post(Runnable) 直接投递一段任务逻辑 Runnable 放到 Message.callback
sendMessage(Message) 传递结构化消息、根据 what 分发逻辑 正常使用 Message.what / obj 等字段

面试说法


7. MessageQueue 为什么更像单链表

一句话MessageQueue 名字叫队列,但从源码实现看,本质是按执行时间排序的单链表。

源码依据

为什么不是普通 FIFO 队列

为什么链表适合

注意


8. 为什么 Looper.loop() 死循环不会一直占满 CPU

一句话:因为没有消息时,线程会阻塞等待,而不是空转。

关键点

底层机制

结论


9. Handler 内存泄漏的原因

一句话:如果 Handler 是 Activity 的非静态内部类,而消息又在队列里延迟存在,就可能通过引用链把 Activity 持有住。

典型引用链

MessageQueue
-> Message
-> Handler
-> Activity

为什么会这样

常见解决方案


10. HandlerThread 是什么

一句话HandlerThread 就是系统帮你封装好的“带 Looper 的子线程”。

作用

常见用法

val handlerThread = HandlerThread("worker")
handlerThread.start()
val handler = Handler(handlerThread.looper)
handler.post {
    // 串行后台任务
}

适用场景


11. 高频追问

一个线程可以有几个 Looper

一个线程可以有几个 Handler

为什么主线程能更新 UI

post(Runnable) 和重写 handleMessage() 谁先执行


12. 一句话速记

知识点 速记
Handler 机制 本质是线程内消息循环和任务调度模型
三者关系 Looper 取消息,MessageQueue 存消息,Handler 发消息和处理消息
主线程 Handler ActivityThread.main() 里系统提前准备好了主线程 Looper
子线程报错 默认没 Loopernew Handler() 会直接异常
MessageQueue 结构 语义上是队列,实现上更像按时间排序的单链表
死循环不空转 没消息时 next() 会阻塞等待
内存泄漏 延迟消息 + 内部类 Handler 可能把 Activity 持有住