Message 是对象,它就是 Java 中的一个普通类,继承自 Object。但 Android 对它做了大量优化,让它在 Handler 机制中高效复用,避免频繁创建和 GC。
一、Message 的本质
publicfinalclassMessageimplementsParcelable{publicintwhat;// 消息标识publicintarg1,arg2;// 轻量整型参数publicObjectobj;// 任意对象参数publicMessengerreplyTo;// 跨进程回复publiclongwhen;// 消息触发时间Bundledata;// 数据载体Handlertarget;// 目标 HandlerRunnablecallback;// 回调 Runnable// 链表指针,用于消息池Messagenext;// 单链表,指向池中的下一个 Message// 同步锁对象privatestaticfinalObjectsPoolSync=newObject();// 消息池头节点privatestaticMessagesPool;// 消息池当前大小privatestaticintsPoolSize=0;// 消息池最大容量privatestaticfinalintMAX_POOL_SIZE=50;}核心点:
- Message 是普通 Java 类,不是基本类型,不是 native 结构
- 但它内部维护了一个静态消息池(sPool),用单链表实现对象复用
二、消息池机制:为什么高效
1. 获取 Message:obtain()
publicstaticMessageobtain(){synchronized(sPoolSync){if(sPool!=null){Messagem=sPool;// 从池头取一个sPool=m.next;// 池头后移m.next=null;// 断开链表m.flags=0;// 重置状态sPoolSize--;// 池大小减1returnm;// 返回复用的对象}}returnnewMessage();// 池空了,才 new}2. 回收 Message:recycle()
publicvoidrecycle(){if(isInUse()){// 正在使用的消息不能回收return;}recycleUnchecked();}voidrecycleUnchecked(){// 重置所有字段flags=FLAG_IN_USE;what=0;arg1=0;arg2=0;obj=null;replyTo=null;sendingUid=-1;workSourceUid=-1;when=0;target=null;callback=null;data=null;synchronized(sPoolSync){if(sPoolSize<MAX_POOL_SIZE){next=sPool;// 头插法:把自己插到池头sPool=this;// 更新池头sPoolSize++;// 池大小加1}}}3. 池结构示意
初始状态:sPool = null, sPoolSize = 0 obtain() 第1次:池空 → new Message() → 返回新对象 obtain() 第2次:池空 → new Message() → 返回新对象 // 两个消息被处理完后 recycle() recycle(msg2): msg2.next = null; sPool = msg2; sPoolSize = 1 recycle(msg1): msg1.next = msg2; sPool = msg1; sPoolSize = 2 // 再次 obtain() obtain(): 取 sPool(msg1); sPool = msg2; sPoolSize = 1 → 复用 msg1 obtain(): 取 sPool(msg2); sPool = null; sPoolSize = 0 → 复用 msg2三、为什么不用 new Message()
| 方式 | 结果 |
|---|---|
new Message() | 堆上分配内存,增加 GC 压力 |
Message.obtain() | 优先从池复用,几乎零分配 |
实际效果:
- 一个高频 Handler(如刷新 UI、动画)每秒发 60 条消息
- 用 obtain():前几次 new 之后,全部从池复用,零 GC
- 用 new Message():每秒 60 个临时对象,频繁 Young GC
四、MessageQueue 中的 Message 是怎么存的
publicfinalclassMessageQueue{MessagemMessages;// 消息队列头,按 when(触发时间)排序的单链表// 入队booleanenqueueMessage(Messagemsg,longwhen){synchronized(this){msg.when=when;Messagep=mMessages;if(p==null||when==0||when<p.when){msg.next=p;// 插到队头mMessages=msg;}else{// 按时间顺序找到插入位置Messageprev;for(;;){prev=p;p=p.next;if(p==null||when<p.when){break;}}msg.next=p;// 插入中间prev.next=msg;}}returntrue;}}关键:MessageQueue 本身也是用Message 的单链表实现,不是数组,不是队列类。Message.next 同时服务于两个链表:
- 消息池链表(复用)
- 消息队列链表(按时间排序)
五、与 Handler、Looper 的关系
Looper.prepare() → 创建 Looper + MessageQueue ↓ Looper.loop() → 无限循环 ├── MessageQueue.next() → 取消息(阻塞/等待) │ └── 没到时间 → nativePollOnce() 休眠 │ └── 到时间了 → 返回 Message ├── msg.target.dispatchMessage(msg) → Handler 处理 │ └── msg.recycleUnchecked() → 回收到消息池 └── 继续循环六、常见误区
| 误区 | 真相 |
|---|---|
| “Message 是基本类型” | ❌ 是对象,但用池复用 |
| “MessageQueue 用数组” | ❌ 用 Message 单链表 |
| “new Message() 和 obtain() 一样” | ❌ obtain() 优先复用,性能差很多 |
| “消息池无限大” | ❌ 最大 50 个,满了直接丢弃不回收 |
| “recycle() 后消息还能用” | ❌ 回收后字段全清空,再用是未定义行为 |
七、最佳实践
// ✅ 正确:用 obtain,复用消息池Messagemsg=Message.obtain(handler,what,arg1,arg2,obj);handler.sendMessage(msg);// ✅ 正确:用便捷方法,内部也是 obtainhandler.obtainMessage(what).sendToTarget();handler.sendEmptyMessage(what);// ❌ 错误:直接 new,浪费内存Messagemsg=newMessage();// 不要这样八、总结
Message 是 Java 对象,不是基本类型,也不是 native 结构。Android 用静态单链表维护了一个最大容量 50 的消息池,通过 obtain() 复用、recycle() 回收,避免高频 Handler 场景下的对象创建和 GC。MessageQueue 本身也是用 Message.next 指针串成的按时间排序的单链表——整个 Handler 机制的核心数据结构,就是这一条 Message 链表在消息池和消息队列之间来回流转。