Android Message源码阅读

之前研究过Handler机制,重点看了Handler、Looper、MessageQueue、ThreadLocal这几个类,其中Message源码没仔细看,现在过一遍。

可以看出message是链表结构。没有pre,单链表。

另外, target就是处理这个消息的Handler对象。when是消息处理时间。callback是处理消息时执行的回调。不详细看了。

再看下消息复用:

sPool意思是消息对象池,存的是一个Message链表头节点。obtain方法从对象池中取头节点。然后断开和池链表的链接。当对象池为空时才new Message()。

再看下recycleUnchecked方法,看下Message何时回收:

这里有几个关键点:

字段清零:防止复用 Message 时携带旧数据,尤其是 target、callback、obj、data;

头插法回池:next = sPool; sPool = this; 把当前正在回收的Message 插到Message消息池链表的头部;

池上限 50:超过就不再缓存,交给 GC。

isAsynchronous方法:

看下在MessageQueue中怎么用的。

这里意思是,被屏障(Barrier)阻塞,查找队列中的下一条异步消息。即,当前消息队列遇到了一个同步屏障,导致普通同步消息无法执行;因此,系统需要跳过这些被阻塞的消息,去查找并执行下一条允许穿过屏障的异步消息。 可以看到这种场景 message的target属性为null,不是app中产生的消息。核心场景是:‌Android 主线程(UI 线程)的消息队列中插入了"同步屏障"(Sync Barrier),且此时需要优先处理高优先级的异步任务(如 UI 绘制、输入事件响应)时。系统框架层(而非普通应用代码)通过调用 MessageQueue.postSyncBarrier() 向消息队列插入这条消息。

典型应用场景:UI 渲染与 VSYNC 信号

这是这段代码最主要、最核心的应用场景。

‌背景问题:‌ Android 屏幕刷新依赖 VSYNC 信号(通常每 16.6ms 一次)。当 VSYNC 信号到来时,系统需要立即执行 UI 的测量、布局、绘制流程(Choreographer 触发的 Traversal)。如果此时主线程消息队列中堆积了大量的普通同步消息(如网络回调、数据库操作、普通的 post(Runnable)),UI 绘制任务就得排队等待,导致掉帧、卡顿。

‌解决方案:‌

在 VSYNC 信号到来前,系统会调用 postSyncBarrier() 插入一个同步屏障。

随后,系统将 UI 绘制任务封装为‌异步消息‌(msg.setAsynchronous(true))并发送入队。

当 Looper 循环取消息时,执行到这段代码:

检测到屏障(target == null)。

‌跳过‌所有后续的普通同步消息(即使它们的时间戳已经到了)。

‌查找并取出‌第一个标记为异步的消息(即 UI 绘制任务)。

UI 绘制任务得以"插队"优先执行,保证在下一个帧周期开始前完成渲染。

绘制完成后,系统调用 removeSyncBarrier() 移除屏障,普通同步消息恢复执行。

相关推荐
RobinXYuan9 天前
【Kafka 源码手记】05 Broker 网络层与请求处理(中):Processor 事件循环与线上的字节
kafka·源码阅读
程序猿阿越15 天前
从 StatefulSet 到容器挂载:CSI 源码走读
后端·kubernetes·源码阅读
嘎嘎风16 天前
MiniMind 学习笔记之 25 Web UI 与 LLaMA-Factory:两条“不写代码”的路径
gpt·llm·源码阅读
RobinXYuan16 天前
【LevelDB 源码阅读】07 | MemTable 的骨架:SkipList
源码阅读·leveldb
RobinXYuan20 天前
【Kafka 源码手记】04 客户端如何找到 Broker
kafka·源码阅读
GeminiJM20 天前
从 0 到 1 学 Codex 源码:Rust 基础(一)
rust·源码阅读·cargo·编程入门·codex
用户0942485680322 天前
第1章:Redis 术语全景与单线程工作原理
redis·快应用·源码阅读
用户0942485680324 天前
Redis 实战修炼与原理进阶专栏大纲
redis·快应用·源码阅读
RobinXYuan24 天前
【Kafka 源码手记】02 Kafka 的核心哲学:Everything Is A Log
kafka·源码阅读