一句话总结:
MessageQueue
是一个以synchronized
保证线程安全的、按时间排序的单向链表,并通过底层的阻塞/唤醒机制,实现了一个高效的多生产者-单消费者消息队列。
一、核心角色:一个多生产者、单消费者的队列
- 多生产者:任何线程都可以通过
Handler
向MessageQueue
中添加消息(enqueueMessage
)。 - 单消费者:只有
Looper
所在的那个线程,能从MessageQueue
中取出消息(next
)。 - 线程安全:通过
synchronized(this)
锁,保证了在任何时刻,只有一个生产者或消费者能修改消息链表,避免了并发冲突。
二、数据结构:为何选择 O(n) 插入的链表?
MessageQueue
内部存储Message
的数据结构是一个按触发时间 (when
) 升序排列的单向链表。
- 插入操作:当一个新消息入队时,必须从头开始遍历链表,找到第一个
when
比它大的消息,然后插入到其前面。因此,时间复杂度为 O(n) 。 - 取出操作:
next()
方法总是从链表头部取出消息,时间复杂度为 O(1) 。
为什么不用理论上更优的最小堆(插入O(log n))?
- n值通常很小:在绝大多数UI场景中,消息队列中的瞬时消息数量非常有限,O(n)遍历的开销完全可以接受。
- 实现的简洁性:链表相对于堆的实现更简单。
- 支持任意删除:链表可以更方便地支持
handler.removeMessages()
这类需要遍历并删除中间节点的操作。
三、效率核心:Looper
的阻塞与唤醒机制
这是MessageQueue
最高效的设计。当Looper
调用next()
方法发现队列为空时,它并不会空转浪费CPU。
- 进入阻塞:
next()
方法会调用nativePollOnce()
,让当前线程进入休眠等待状态。 - 触发唤醒:当另一个线程调用
enqueueMessage()
向空队列中添加消息时,它会发现Looper
正在“沉睡”,于是调用nativeWake()
来唤醒它。 - 继续工作:被唤醒的
Looper
线程从nativePollOnce()
返回,再次尝试从队列中取消息,此时就能成功取到并执行。
这种基于底层pipe/epoll的机制,保证了主线程在空闲时几乎不占用CPU资源。
四、特殊机制:同步屏障(Synchronization Barrier)
同步屏障是一种特殊的Message
(其target
为null
),它用于改变消息处理的优先级。
- 作用:当
next()
方法在遍历链表时遇到同步屏障,它会忽略所有普通的同步消息,转而专门寻找并执行队列中被标记为**异步(isAsynchronous() == true
)**的消息。 - 应用场景:主要用于UI渲染。当请求一个
View
重绘时,系统会插入一个同步屏障,并发送一个异步的绘制消息。这样可以确保绘制任务(异步消息)能够绕过其他低优先级的同步消息,被优先执行,从而保证界面的流畅性。
总结
- 数据结构:按时间排序的单向链表,插入复杂度为 O(n) 。
- 线程安全:生产者(
enqueueMessage
)和消费者(next
)都使用synchronized
关键字来保护共享的链表数据。 - 高效设计:通过底层的阻塞/唤醒机制避免了CPU空转,是
Looper
高效工作的基石。 - 优先级调度:通过同步屏障机制,为高优先级的异步任务(如UI渲染)提供了“插队”的能力。