Android随笔-Handler的Message是什么结构

📅 2026/7/24 0:08:18 👁️ 阅读次数 📝 编程学习
Android随笔-Handler的Message是什么结构

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 链表在消息池和消息队列之间来回流转。