Android Message创建最佳实践:obtain()为何优于new()

273 阅读3分钟

一句话总结:

创建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++;
            }
        }
    }
}

源码揭示的真相:

  1. obtain()先借后造:它会先尝试从sPool链表中“借”一个已回收的对象,只有在“借”不到时,才会new一个。
  2. recycleUnchecked()自动归还:当Looper处理完一个消息后,会自动调用此方法,将该消息对象“还”回对象池的头部,等待下一次被obtain()
  3. 线程安全:所有对池的操作都在synchronized代码块中,保证了多线程环境下的安全性。

三、Message的“五脏六腑”:如何高效传递数据

Message不仅仅是一个空壳,它提供了多种传递数据的字段,选择合适的字段能提升效率。

字段类型用途性能提示
whatint消息标识。通常用于switch语句,区分消息类型。-
arg1, arg2int传递整型数据极高。避免了创建Integer对象的开销(自动装箱)。
objObject传递任意对象性能稍低,会增加一个对象引用。
setData(Bundle)Bundle传递复杂的键值对数据相比obj,开销更大,适用于需要传递多个值的场景。
callbackRunnable存放一个Runnable任务handler.post(runnable)的底层实现。

最佳实践:当需要传递一到两个简单的整型值时,请务必使用arg1arg2,这是最高效的方式。


四、“无消息”的消息: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;
}

HandlerdispatchMessage(Message msg)中分发消息时,它会优先检查msg.callback是否为null

  • 如果不为null,它会直接执行callback.run(),而不会再调用handleMessage()
  • 如果为null,它才会调用我们熟悉的handleMessage()

结论handler.post()只是一个语法糖,它让我们可以用更简洁的方式,将一个Runnable任务封装在Message中,发送到目标线程执行。


五、最终选型指南

  1. 只需在目标线程执行一个动作

    • 首选handler.post(Runnable)。代码最简洁,意图最明确。
  2. 需要传递数据或区分消息类型

    • 首选handler.obtainMessage(int what, int arg1, int arg2, Object obj) 的各种重载版本,一步到位。
    • 备选Message.obtain(),然后手动设置各个字段。
  3. 何时使用 new Message()

    • 几乎永不。除非你在编写脱离Android框架的、且对性能无要求的纯Java代码。