理解 Handler 机制不仅要看消息如何分发,更要理清各组件之间的强引用(持有)关系。这不仅是面试的高频考点,也是分析内存泄漏的底层基础。
1. 核心对象持有关系一览表
| 持有者 (Owner) | 被持有者 (Owned) | 持有方式 | 作用与意义 |
|---|---|---|---|
| Thread | Looper | ThreadLocal |
确保每个线程最多只有一个 Looper 实例,且线程私有。 |
| Looper | MessageQueue | 成员变量 mQueue |
Looper 循环时不断从这个队列中通过 next() 取消息。 |
| Handler | Looper | 成员变量 mLooper |
Handler 发送消息时需要知道往哪个 Looper 的队列里投递。 |
| Handler | MessageQueue | 成员变量 mQueue |
性能优化,避免每次发消息都通过 Looper 去间接获取。 |
| MessageQueue | Message | 链表结构 mMessages |
消息队列本质上是一个以 Message 为节点的单向链表。 |
| Message | Handler | 成员变量 target |
核心点:记录该消息由谁发送,以便 Looper 取出后回调正确的 dispatchMessage。 |
2. 深度解析:为什么会内存泄漏?
通过上面的持有关系,我们可以推导出一条“致命”的引用链:
主线程 (Thread)
↓ (ThreadLocal)
Looper
↓ (mQueue)
MessageQueue
↓ (mMessages 链表)
Message (尚未到期的延时消息)
↓ (target)
Handler (匿名内部类/非静态内部类)
↓ (隐式持有)
Activity
结论:只要消息队列中还有一个 target 指向该 Handler 的消息没处理完,整个 Activity 就无法被 GC 回收。
3. Handler 与 Looper 的解耦设计
虽然 Handler 持有 Looper,但它们的生命周期是不同的:
| 组件 | 生命周期 | 数量关系 |
|---|---|---|
| Looper | 与线程同生共死。 | 一个线程只能有 1 个。 |
| Handler | 随业务逻辑创建与销毁。 | 一个线程可以对应 N 个。 |
设计精妙之处:
Handler 在构造时绑定的 Looper 决定了它“在哪个线程处理消息”。这种设计允许我们在子线程 A 中创建 Handler 并在构造时传入主线程的 Looper,从而实现“子线程发消息,主线程收消息”的跨线程切换。
4. 关键代码支撑
1 | // Handler.java 构造函数 |
5. 总结
Handler 机制的持有关系是一个闭环:
- Thread 通过
ThreadLocal守住 Looper。 - Looper 维护 MessageQueue。
- Handler 往 MessageQueue 塞入 Message。
- Message 反向持有 Handler。
理解了这个闭环,就理解了 Android 异步消息处理的灵魂,也掌握了规避内存泄漏的钥匙。