在理解这次重构之前,我们先把时间线拉开。
一、Handler 的四次进化
如果把 MessageQueue 放到时间轴上看,你会发现: 它并不是一套一开始就设计好的系统,而是四代架构不断演进、逐层叠加的结果。
🧱 Android 1.5(纯 Java 期)
能用就行
其实很多人不知道这个版本,在行为上类似一个 BlockingQueue:
生产者投递消息 消费者阻塞等待
- 单链表
wait / notifysynchronized
👉 本质:解决线程间通信
笔者在这个版本写过N个APP。
⚙️ Android 2.3(Native 期)
接入操作系统能力
这是一个被很多人低估的版本升级。
- 引入
epoll - Looper 能监听 FD(输入、Binder 等)
意味着什么?
Looper 不再只是"取消息",而是可以同时监听:
输入事件(触摸 / 键盘) Binder 回调 VSYNC 信号 Socket / Pipe
👉 本质:从"消息循环"升级为"事件循环"
🎯 Android 4.1(异步期)
为流畅度而战
这是 Handler 历史上最关键的一次改动之一。
在 Android 4.1 之前,系统普遍存在:
- UI 卡顿
- 掉帧
- 滑动不流畅
原因很简单
普通消息 和 UI 渲染消息 是同一优先级
💥 典型问题
大量业务消息 → 堆满队列
→ VSYNC 渲染消息排在后面
→ UI 延迟执行
→ 掉帧
🚧 解决方案:同步屏障(Sync Barrier)
系统在队列中插入一个"屏障":
屏障之后: ❌ 同步消息被拦住 ✅ 异步消息可以穿透
⚡ 配套机制:异步消息(Async Message)
标记关键消息: 这是高优先级,不要被挡
🔗 组合效果
UI 渲染(VSYNC) → 标记为异步 → 永远不会被普通消息阻塞
🎯 这一步的意义
👉 UI 渲染第一次拥有"绝对优先级"
🚀 直接带来一个时代
🚀 60fps(16ms 帧预算)
💥 Android 17(无锁期)
面向多核时代的彻底重构
- DeliQueue
- Lock-Free
- 三层结构
👉 本质:解决高并发下的锁竞争
一句话串起来:

二、为什么必须重构?
旧版 MessageQueue 的问题不在于"写得不好",而在于:
❗ 它生于单核时代,却跑在多核时代
它的结构是:
- 单链表
synchronized全局锁
带来的问题:
❌ 1. 入队 O(n)
需要遍历找插入位置
❌ 2. 所有线程争抢一把锁
- 发消息要锁
- 删除要锁
- barrier 也要锁
❌ 3. 优先级反转

Google 用 BigTrace 在大量设备上统计后发现:
👉 这是系统性问题,而不是个别 App Bug
结论只有一个:
❌ 不能优化 ✅ 必须换模型
三、DeliQueue 的核心思想
核心洞察只有一句话:
生产者和消费者的需求根本不同
🧵 生产者(任意线程)
只关心:
- 快速投递
- 不阻塞
👉 不关心顺序
🧠 消费者(Looper)
只关心:
- 按时间有序执行
👉 且只有一个线程,无竞争
那问题来了:
❓ 为什么要用一个结构 + 一把锁,同时满足这两种需求?
四、三层架构:彻底拆分问题
于是,MessageQueue 被拆成三层:

一句话理解:
👉 写的时候不排序,读的时候再排序
五、MessageStack:Lock-Free 并发入口
MessageStack 是所有生产者线程的共同入口,它的核心是一个 Treiber 无锁栈。
Treiber 栈的入队逻辑极其简洁:

没有锁,没有等待。失败了就重试,因为失败意味着另一个线程刚刚成功入队了。
👉 至少有一个线程在前进
它的职责只有一件事:
把并发问题压缩成一个 O(1) 的 CAS 操作
至于顺序?不管。
heapSweep:从 Stack 到 Heap 的批量转移
Looper 每次取消息前,会做一件事:
👉 把 Stack 里的消息"扫"进 Heap
关键优化:
✅ 只处理新数据
通过 mLooperProcessed 游标避免重复扫描
✅ 顺手构建双向链表
为后续 O(1) 删除做准备
本质:
👉 延迟排序(Lazy Ordering)
六、MessageHeap:单线程有序调度核心
这里才是真正"排队"的地方。
结构:
👉 最小堆(按 when 排序)
关键点:
❗ 只有 Looper 能访问
结果:
- 无锁
- 无竞争
- 高性能
为什么是两个堆?
不是一个,而是:
mSyncHeapmAsyncHeap
解决的问题:
👉 Sync Barrier
旧版:

新版:

一句话:
👉 用结构替代逻辑
七、删除机制:墓碑 + freelist
删除最难,因为:
- 来自任意线程
- Heap 只能 Looper 改
解决方案:
① 逻辑删除
② 加入 freelist

③ Looper 统一清理

本质:
❗ 跨线程操作 → 延迟为单线程操作
八、WaitState:无锁唤醒协议
核心变量:

两种模式:
💤 Deadline(Looper 在睡)
- 存唤醒时间
👉 有更早消息 → 唤醒
🏃 Counter(Looper 在跑)
- 存计数
👉 不唤醒
本质:
👉 一个变量表达整个系统状态
九、insertSeq:保证 FIFO
Treiber 栈无序 → 怎么保证顺序?
解法:

规则:
- 普通消息:递增
- 插队消息:负数递减
效果:
bash
when 相同 → 按 seq 排
一句话:
👉 用数字替代逻辑
十、VarHandle:性能关键
不用 Atomic 的原因:

实际使用:
- Acquire(读)
- Release(写)
效果:
👉 更轻量,更快
十一、整体架构图

十二、本质差异
| 维度 | 旧版 | DeliQueue |
|---|---|---|
| 并发 | 锁 | 无锁 |
| 入队 | 链表 O(n) | 栈 O(1) |
| 排序 | 入队时 | 出队前 |
| 删除 | 立即 | 延迟 |
| barrier | 遍历 | 双堆 |
| 唤醒 | notify | CAS |
十三、设计哲学总结
这套系统背后其实只有五个原则:
① 拆
并发 ≠ 排序
② 拖
能晚做就晚做
③ 压
复杂协调用 CAS 表达
④ 单
有序只交给一个线程
⑤ 弱
只用最弱内存语义
十四、最终总结

这是 Android 20 年来:
最重要的一次消息机制重构之一
总结
DeliQueue 做的不是优化,而是把"锁"这个概念,从 MessageQueue 里删除了。
那么MessageQueue 都去除了重锁,那么咱们的应用层就更不该用重锁了。
