一句话总结:
创建Message对象的最佳实践是调用Message.obtain()而非new Message(),核心原因在于利用系统内置的对象池机制来复用对象,从而避免高频操作下的内存抖动与性能损耗。
一、三种方式与一个原则
new Message(): 直接创建新对象,会频繁触发GC,不推荐。Message.obtain(): 从全局消息池中获取,推荐。Handler.obtainMessage(): 内部调用Message.obtain()并关联Handler,最推荐。
核心原则: 永远优先使用obtain()系列方法获取Message实例。
二、深入源码:Message对象池的庐山真面目
Message类内部维护了一个静态的、链表结构的对象池,其工作机制可以通过简化后的源码来理解:
public final class Message {
// 指向对象池头部的静态变量
private static Message sPool;
private static final Object sPoolSync = new Object();
private static int sPoolSize = 0;
// 对象池的最大容量,通常是50
private static final int MAX_POOL_SIZE = 50;
// 下一个池内对象的引用
Message next;
public static Message obtain() {
synchronized (sPoolSync) {
// 如果池中有对象
if (sPool != null) {
// 从池中取出头部的对象
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
// 如果池为空,才创建一个新对象
return new Message();
}
void recycleUnchecked() {
// ... 清理Message的内部字段 ...
synchronized (sPoolSync) {
// 如果池未满
if (sPoolSize < MAX_POOL_SIZE) {
// 将当前对象作为新的头部,加入池中
this.next = sPool;
sPool = this;
sPoolSize++;
}
}
}
}
源码揭示的真相:
obtain()先借后造:它会先尝试从sPool链表中“借”一个已回收的对象,只有在“借”不到时,才会new一个。recycleUnchecked()自动归还:当Looper处理完一个消息后,会自动调用此方法,将该消息对象“还”回对象池的头部,等待下一次被obtain()。- 线程安全:所有对池的操作都在
synchronized代码块中,保证了多线程环境下的安全性。
三、Message的“五脏六腑”:如何高效传递数据
Message不仅仅是一个空壳,它提供了多种传递数据的字段,选择合适的字段能提升效率。
| 字段 | 类型 | 用途 | 性能提示 |
|---|---|---|---|
what | int | 消息标识。通常用于switch语句,区分消息类型。 | - |
arg1, arg2 | int | 传递整型数据。 | 极高。避免了创建Integer对象的开销(自动装箱)。 |
obj | Object | 传递任意对象。 | 性能稍低,会增加一个对象引用。 |
setData(Bundle) | Bundle | 传递复杂的键值对数据。 | 相比obj,开销更大,适用于需要传递多个值的场景。 |
callback | Runnable | 存放一个Runnable任务。 | handler.post(runnable)的底层实现。 |
最佳实践:当需要传递一到两个简单的整型值时,请务必使用arg1和arg2,这是最高效的方式。
四、“无消息”的消息:handler.post(Runnable)的真相
我们常用的handler.post(runnable)方法,看似与Message无关,但其背后正是Message机制在驱动。
post(Runnable)方法的简化逻辑如下:
Java
public final boolean post(Runnable r) {
// 1. 获取一个Message对象
// 2. 将传入的Runnable存入Message的callback字段
// 3. 发送这个Message
return sendMessage(getPostMessage(r));
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
当Handler在dispatchMessage(Message msg)中分发消息时,它会优先检查msg.callback是否为null。
- 如果不为
null,它会直接执行callback.run(),而不会再调用handleMessage()。 - 如果为
null,它才会调用我们熟悉的handleMessage()。
结论:handler.post()只是一个语法糖,它让我们可以用更简洁的方式,将一个Runnable任务封装在Message中,发送到目标线程执行。
五、最终选型指南
-
只需在目标线程执行一个动作?
- 首选:
handler.post(Runnable)。代码最简洁,意图最明确。
- 首选:
-
需要传递数据或区分消息类型?
- 首选:
handler.obtainMessage(int what, int arg1, int arg2, Object obj)的各种重载版本,一步到位。 - 备选:
Message.obtain(),然后手动设置各个字段。
- 首选:
-
何时使用
new Message()?- 几乎永不。除非你在编写脱离Android框架的、且对性能无要求的纯Java代码。