Message 对象池原理

6 阅读16分钟

Message 对象池原理

面试重要度:⭐⭐⭐⭐

考察频率:字节 70% | 阿里 65% | 腾讯 60%

一、核心概念

1.1 定义与作用

一句话定义: Message 对象池是 Android 消息机制中采用的享元模式实现,通过维护一个静态单链表缓存已回收的 Message 对象,避免频繁创建和销毁 Message 带来的内存分配开销和 GC 压力。

为什么重要

  • Handler 消息机制是 Android 最核心的通信机制,消息发送极其频繁
  • 每秒可能产生数十甚至上百条消息(UI 刷新、事件传递等)
  • 频繁 new Message 会导致大量短生命周期对象,触发 GC 影响性能
  • 面试考察点:享元模式实践、Android 性能优化思想、源码细节理解

设计目标

无对象池:new Message → 使用 → GC 回收 → new Message → ...(频繁 GC)
有对象池:obtain() → 使用 → recycle() → obtain() → ...(复用对象,零 GC)

1.2 与其他概念的关系

Handler.sendMessage(msg)
        ↓
Message.obtain()  ← 本文重点:从对象池获取
        ↓
enqueueMessage() → 消息入队(详见:../03-MessageQueue队列管理/)
        ↓
next() → 消息出队
        ↓
dispatchMessage() → 消息处理
        ↓
msg.recycleUnchecked()  ← 本文重点:回收到对象池
  • 消息队列:Message 在队列中流转,最终被回收到对象池
  • Looper:loop() 中处理完消息后调用 recycleUnchecked()
  • 本文重点:obtain() 获取、recycle() 回收、对象池数据结构

二、核心原理

2.1 工作机制

整体流程

obtain() 获取 → 设置消息内容 → 入队等待 → 出队处理 → recycleUnchecked() 回收

对象池数据结构

sPool (静态链表头)
   ↓
Message1 → Message2 → Message3 → ... → MessageN → null
   │           │           │               │
  next        next        next            next
  • 单链表结构,sPool 指向链表头
  • 每个 Message 的 next 字段指向下一个空闲 Message
  • sPoolSize 记录当前池中对象数量
  • MAX_POOL_SIZE = 50,限制池大小

2.2 源码分析

2.2.1 Message 类核心字段

// Android 11 源码:frameworks/base/core/java/android/os/Message.java

public final class Message implements Parcelable {
    // 消息标识
    public int what;
    public int arg1;
    public int arg2;
    public Object obj;

    // 执行时间(绝对时间戳)
    /*package*/ long when;

    // 目标 Handler
    /*package*/ Handler target;

    // 回调 Runnable
    /*package*/ Runnable callback;

    // 链表指针(用于 MessageQueue 和对象池)
    /*package*/ Message next;

    // ========== 对象池相关 ==========

    // 对象池链表头(静态,全局共享)
    private static Message sPool;

    // 对象池当前大小
    private static int sPoolSize = 0;

    // 对象池最大容量
    private static final int MAX_POOL_SIZE = 50;

    // 同步锁
    private static final Object sPoolSync = new Object();

    // 标记位:是否正在使用
    private static final int FLAG_IN_USE = 1 << 0;

    // 标记位:是否是异步消息
    private static final int FLAG_ASYNCHRONOUS = 1 << 1;

    // 标记字段
    /*package*/ int flags;
}

源码解读

  • sPool:静态变量,指向空闲 Message 链表的头节点
  • next:复用字段,在队列中指向下一条消息,在对象池中指向下一个空闲对象
  • FLAG_IN_USE:防止正在使用的 Message 被重复使用或回收
  • sPoolSync:同步锁,保证多线程安全

2.2.2 obtain() - 获取消息

// Android 11 源码:frameworks/base/core/java/android/os/Message.java

/**
 * 从对象池获取一个 Message,比 new Message() 更高效
 */
public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            // 步骤1:从链表头取出一个 Message
            Message m = sPool;

            // 步骤2:更新链表头指向下一个
            sPool = m.next;

            // 步骤3:断开取出的 Message 与链表的连接
            m.next = null;

            // 步骤4:清除 IN_USE 标记(之前 recycle 时清除了,这里确保)
            m.flags = 0;

            // 步骤5:减少池大小计数
            sPoolSize--;

            return m;
        }
    }
    // 步骤6:池为空,创建新对象
    return new Message();
}

源码解读

  • 优先从对象池获取,池空时才 new
  • 同步块保证线程安全
  • 取出后清除 flags,确保干净的状态
  • 时间复杂度 O(1)

2.2.3 obtain() 的重载方法

// 指定 Handler
public static Message obtain(Handler h) {
    Message m = obtain();
    m.target = h;
    return m;
}

// 指定 Handler 和 Runnable
public static Message obtain(Handler h, Runnable callback) {
    Message m = obtain();
    m.target = h;
    m.callback = callback;
    return m;
}

// 指定 Handler 和 what
public static Message obtain(Handler h, int what) {
    Message m = obtain();
    m.target = h;
    m.what = what;
    return m;
}

// 复制另一个 Message
public static Message obtain(Message orig) {
    Message m = obtain();
    m.what = orig.what;
    m.arg1 = orig.arg1;
    m.arg2 = orig.arg2;
    m.obj = orig.obj;
    // ... 其他字段复制
    return m;
}

// 完整参数
public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj) {
    Message m = obtain();
    m.target = h;
    m.what = what;
    m.arg1 = arg1;
    m.arg2 = arg2;
    m.obj = obj;
    return m;
}

2.2.4 recycle() - 回收消息

// Android 11 源码:frameworks/base/core/java/android/os/Message.java

/**
 * 将 Message 回收到对象池
 * 注意:调用后不能再使用该 Message
 */
public void recycle() {
    // 检查是否正在使用
    if (isInUse()) {
        if (gCheckRecycle) {
            throw new IllegalStateException("This message cannot be recycled because it "
                    + "is still in use.");
        }
        return;
    }
    recycleUnchecked();
}

/**
 * 内部回收方法,不检查使用状态(由系统调用)
 */
void recycleUnchecked() {
    // 步骤1:清空所有字段,准备复用
    flags = FLAG_IN_USE;  // 标记为"正在回收",防止重复回收
    what = 0;
    arg1 = 0;
    arg2 = 0;
    obj = null;
    replyTo = null;
    sendingUid = UID_NONE;
    workSourceUid = UID_NONE;
    when = 0;
    target = null;
    callback = null;
    data = null;

    synchronized (sPoolSync) {
        // 步骤2:检查池是否已满
        if (sPoolSize < MAX_POOL_SIZE) {
            // 步骤3:插入到链表头部
            next = sPool;
            sPool = this;

            // 步骤4:增加池大小计数
            sPoolSize++;
        }
        // 池已满则丢弃,让 GC 回收
    }
}

源码解读

  • recycle():供外部调用,有安全检查
  • recycleUnchecked():系统内部调用,无检查,更高效
  • 清空所有字段防止内存泄漏(特别是 obj、callback)
  • 池满时直接丢弃,不会无限增长
  • 头插法,时间复杂度 O(1)

2.2.5 isInUse() - 使用状态检查

/*package*/ boolean isInUse() {
    return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
}

/*package*/ void markInUse() {
    flags |= FLAG_IN_USE;
}

2.2.6 Looper.loop() 中的回收调用

// Android 11 源码:frameworks/base/core/java/android/os/Looper.java

public static void loop() {
    final Looper me = myLooper();
    final MessageQueue queue = me.mQueue;

    for (;;) {
        Message msg = queue.next();
        if (msg == null) {
            return;
        }

        try {
            // 分发消息
            msg.target.dispatchMessage(msg);
        } finally {
            // 重要:消息处理完毕后回收
            msg.recycleUnchecked();
        }
    }
}

2.3 对象池设计细节

细节1:为什么用链表而不是数组?

特性链表数组
插入/删除O(1) 头插法O(1) 但需要索引管理
内存占用按需分配预分配固定大小
复用 next 字段可以需要额外索引数组
实现复杂度简单较复杂

Message 已有 next 字段(MessageQueue 用),复用它实现对象池最高效。

细节2:MAX_POOL_SIZE = 50 的设计考量

private static final int MAX_POOL_SIZE = 50;
  • 太小:复用效果差,仍然频繁 new
  • 太大:占用过多内存
  • 50 是经验值,覆盖大多数场景的峰值消息数

细节3:线程安全设计

private static final Object sPoolSync = new Object();

// 所有对象池操作都在同步块中
synchronized (sPoolSync) {
    // ...
}
  • 多个线程可能同时 obtain() 和 recycle()
  • 使用独立的锁对象,不影响 Message 其他操作

细节4:FLAG_IN_USE 的作用

// 入队时标记
msg.markInUse();  // flags |= FLAG_IN_USE

// 回收时检查
if (isInUse()) {
    throw new IllegalStateException(...);
}

// 获取时清除
m.flags = 0;

防止场景:

  • 消息正在队列中等待处理时被 recycle()
  • 同一个 Message 被多次 recycle()

边界情况:池满时的处理

if (sPoolSize < MAX_POOL_SIZE) {
    next = sPool;
    sPool = this;
    sPoolSize++;
}
// else: 直接丢弃,让 GC 回收
  • 不会抛异常
  • 不会阻塞
  • 多余的 Message 自然被 GC

三、实际应用

3.1 典型场景

场景1:发送简单消息

// 推荐:使用 obtain()
Message msg = Message.obtain();
msg.what = MSG_UPDATE_UI;
msg.arg1 = progress;
handler.sendMessage(msg);

// 或者更简洁
Message msg = Message.obtain(handler, MSG_UPDATE_UI);
msg.arg1 = progress;
handler.sendMessage(msg);

场景2:发送带回调的消息

// 使用 obtain 创建带 Runnable 的消息
Message msg = Message.obtain(handler, () -> {
    updateUI();
});
handler.sendMessage(msg);

// 等价于
handler.post(() -> updateUI());

场景3:复制消息

// 需要发送相同内容的消息到多个 Handler
Message original = Message.obtain();
original.what = MSG_BROADCAST;
original.obj = data;

handler1.sendMessage(Message.obtain(original));
handler2.sendMessage(Message.obtain(original));

original.recycle();  // 原始消息手动回收

3.2 最佳实践

推荐做法

  1. 始终使用 Message.obtain() 而非 new Message()

    // 推荐
    Message msg = Message.obtain();
    
    // 不推荐
    Message msg = new Message();
    
  2. 使用 Handler 的便捷方法

    // 这些方法内部使用 obtain()
    handler.obtainMessage(what);
    handler.obtainMessage(what, arg1, arg2);
    handler.obtainMessage(what, obj);
    
  3. 不要手动调用 recycle()

    // 错误:系统会自动回收
    handler.sendMessage(msg);
    msg.recycle();  // 危险!消息可能还在队列中
    
    // 正确:让系统处理
    handler.sendMessage(msg);
    // Looper.loop() 会在处理完后自动调用 recycleUnchecked()
    
  4. 特殊情况需要手动回收

    // 场景:创建了消息但决定不发送
    Message msg = Message.obtain();
    if (shouldSend) {
        handler.sendMessage(msg);
    } else {
        msg.recycle();  // 不发送时需要手动回收
    }
    

常见错误

  1. 发送后继续使用 Message

    // 错误
    Message msg = Message.obtain();
    msg.what = 1;
    handler.sendMessage(msg);
    msg.arg1 = 100;  // 危险!消息可能已被回收或正在处理
    
    // 正确
    Message msg = Message.obtain();
    msg.what = 1;
    msg.arg1 = 100;  // 先设置完所有字段
    handler.sendMessage(msg);  // 最后发送
    
  2. 重复发送同一个 Message

    // 错误
    Message msg = Message.obtain();
    handler.sendMessage(msg);
    handler.sendMessage(msg);  // 抛出异常:This message is already in use
    
    // 正确
    handler.sendMessage(Message.obtain(handler, what));
    handler.sendMessage(Message.obtain(handler, what));
    
  3. 持有 Message 引用

    // 错误
    private Message cachedMsg;
    
    void send() {
        if (cachedMsg == null) {
            cachedMsg = Message.obtain();
        }
        handler.sendMessage(cachedMsg);  // 第二次调用会出问题
    }
    
    // 正确:每次都 obtain
    void send() {
        handler.sendMessage(Message.obtain(handler, what));
    }
    

3.3 性能优化建议

1. 避免高频创建新 Message

// 性能差:每次都 new
for (int i = 0; i < 1000; i++) {
    handler.sendMessage(new Message());  // 1000 次内存分配
}

// 性能好:使用对象池
for (int i = 0; i < 1000; i++) {
    handler.sendMessage(Message.obtain());  // 大部分复用
}

2. 合理使用 Handler 便捷方法

// sendEmptyMessage 内部会 obtain
handler.sendEmptyMessage(MSG_UPDATE);

// 等价于
Message msg = Message.obtain();
msg.what = MSG_UPDATE;
handler.sendMessage(msg);

四、面试真题解析

4.1 基础必答题


【高频题1】 为什么要使用 Message.obtain() 而不是 new Message()?

标准答案(30秒) : Message.obtain() 使用对象池复用 Message 对象,避免频繁创建新对象。Android 消息机制中消息发送非常频繁,每秒可能数十上百条。使用对象池可以减少内存分配和 GC 压力,提升性能。这是享元模式在 Android 中的典型应用。

深入展开

对象池实现:

public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;      // 从链表头取出
            sPool = m.next;         // 更新链表头
            m.next = null;
            m.flags = 0;
            sPoolSize--;
            return m;
        }
    }
    return new Message();           // 池空才 new
}

面试官追问

  • 追问1:对象池是如何实现的?

    • 答:使用静态单链表,sPool 指向链表头,MAX_POOL_SIZE=50 限制大小。obtain() 头部取出,recycle() 头部插入,时间复杂度都是 O(1)。
  • 追问2:对象池是线程安全的吗?

    • 答:是的。使用 synchronized(sPoolSync) 同步块保护所有池操作,sPoolSync 是专门的锁对象。

【高频题2】 Message 什么时候被回收到对象池?

标准答案(30秒) : Message 在 Looper.loop() 处理完消息后自动回收。具体是在 dispatchMessage() 执行完毕后,finally 块中调用 msg.recycleUnchecked() 将消息回收到对象池。开发者通常不需要手动调用 recycle()。

深入展开

// Looper.loop()
for (;;) {
    Message msg = queue.next();
    try {
        msg.target.dispatchMessage(msg);
    } finally {
        msg.recycleUnchecked();  // 自动回收
    }
}

面试官追问

  • 追问1:什么情况需要手动调用 recycle()?

    • 答:当创建了 Message 但决定不发送时,需要手动回收。例如条件判断后不发送的场景。
  • 追问2:recycle() 和 recycleUnchecked() 有什么区别?

    • 答:recycle() 会检查 FLAG_IN_USE,如果消息正在使用会抛异常。recycleUnchecked() 是系统内部使用,不做检查,更高效。

【高频题3】 Message 对象池的最大容量是多少?满了怎么办?

标准答案(30秒) : 最大容量是 50(MAX_POOL_SIZE)。池满时,recycleUnchecked() 不会将消息放入池中,直接丢弃让 GC 回收。这个设计保证了对象池不会无限增长占用过多内存,同时覆盖了大多数正常使用场景。

深入展开

void recycleUnchecked() {
    // ... 清空字段 ...
    synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {  // 检查是否已满
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
        // 满了就丢弃,不做任何处理
    }
}

面试官追问

  • 追问1:50 这个值是怎么确定的?

    • 答:经验值,覆盖大多数场景的峰值消息数。太小复用效果差,太大浪费内存。
  • 追问2:如果应用消息特别多,50 够用吗?

    • 答:通常够用。即使不够,只是部分 Message 无法复用需要 new,不会影响功能。而且消息处理很快,池会很快有空位。

【高频题4】 为什么 Message 用单链表实现对象池而不是数组?

标准答案(30秒) : 因为 Message 类本身已有 next 字段用于 MessageQueue 的链表结构,对象池复用这个字段实现,不需要额外空间。单链表的头插头取操作都是 O(1),且不需要预分配固定大小的数组,内存使用更灵活。

深入展开

public final class Message {
    /*package*/ Message next;  // 复用字段
    // 在 MessageQueue 中:指向队列中下一条消息
    // 在对象池中:指向池中下一个空闲 Message
}

面试官追问

  • 追问1:next 字段在队列和对象池中如何区分使用?

    • 答:通过 FLAG_IN_USE 标记。消息在队列中时 FLAG_IN_USE=1,回收到池中时清空所有字段包括 flags,此时 next 用于链接池中其他对象。
  • 追问2:头插法有什么好处?

    • 答:时间复杂度 O(1),且刚回收的对象最快被复用(热数据),CPU 缓存命中率更高。

【高频题5】 发送消息后还能修改 Message 的内容吗?

标准答案(30秒) : 不能,这是危险操作。发送后 Message 可能已经在队列中等待处理,或者正在被处理,甚至已经被回收到对象池。此时修改可能导致数据错乱或崩溃。应该在 sendMessage() 前设置好所有字段。

深入展开

// 错误示例
Message msg = Message.obtain();
handler.sendMessage(msg);
msg.what = 1;  // 危险!

// 正确做法
Message msg = Message.obtain();
msg.what = 1;
msg.arg1 = 2;
handler.sendMessage(msg);  // 设置完再发送

面试官追问

  • 追问1:为什么发送后不能修改?

    • 答:sendMessage() 只是把消息放入队列,不是同步执行。消息可能被其他线程的 Looper 取出处理,此时修改会有线程安全问题。
  • 追问2:如果确实需要修改已发送的消息怎么办?

    • 答:使用 removeMessages() 移除旧消息,发送新消息。或者让 Handler 持有共享数据,消息只传递通知。

4.2 进阶加分题


【进阶题1】 分析:Message 对象池是全局共享的吗?有什么影响?

参考答案

是的,sPool 是静态变量,全进程共享一个对象池:

private static Message sPool;  // static
private static int sPoolSize = 0;
private static final Object sPoolSync = new Object();

影响分析:

  1. 优点

    • 复用效率最大化,任何线程的 Message 都能复用
    • 内存占用可控,只维护一个池
  2. 潜在问题

    • 多线程竞争:高并发时 synchronized 可能成为瓶颈

    • 但实际影响很小:

      • 同步块内操作极简单(几个指针操作)
      • 50 个对象的池足够大多数场景
  3. 为什么不用 ThreadLocal?

    • 每个线程一个池会占用更多内存
    • 消息可能跨线程传递(子线程发送,主线程接收)
    • 全局池的复用效率更高

追问:如何验证对象池确实减少了 GC?

  • 答:使用 Android Profiler 的内存分析,对比使用 obtain() 和 new Message() 的内存分配次数和 GC 频率。

【进阶题2】 如果多次调用 recycle() 会发生什么?

参考答案

分析 recycle() 源码:

public void recycle() {
    if (isInUse()) {  // 检查 FLAG_IN_USE
        if (gCheckRecycle) {
            throw new IllegalStateException("This message cannot be recycled because it "
                    + "is still in use.");
        }
        return;  // 或者直接返回
    }
    recycleUnchecked();
}

第一次 recycle():

  • FLAG_IN_USE 被清除
  • Message 放入池中

第二次 recycle():

  • isInUse() 返回 false(已清除)
  • recycleUnchecked() 再次执行
  • Message 再次插入池头
  • 导致:同一个 Message 在池中出现两次!

后果:

Message m1 = Message.obtain();  // 取出这个 Message
Message m2 = Message.obtain();  // 又取出同一个 Message!
// m1 == m2,导致数据混乱

追问:系统如何避免这个问题?

  • 答:recycleUnchecked() 在清空字段时会设置 flags = FLAG_IN_USE,这样第二次 recycle() 时 isInUse() 返回 true。但这是个 hack,最好的做法是开发者不要手动调用 recycle()。

【进阶题3】 设计题:如果让你优化 Message 对象池,你会怎么做?

参考答案

当前设计的不足:

  1. 全局锁可能成为高并发瓶颈
  2. 固定大小 50 可能不够灵活

优化方案:

方案1:分段锁

// 多个小池,按线程 ID 哈希选择
private static final int POOL_COUNT = 4;
private static Message[] sPools = new Message[POOL_COUNT];
private static int[] sPoolSizes = new int[POOL_COUNT];
private static Object[] sPoolSyncs = new Object[POOL_COUNT];

public static Message obtain() {
    int index = (int) (Thread.currentThread().getId() % POOL_COUNT);
    synchronized (sPoolSyncs[index]) {
        // 从对应的池获取
    }
}

方案2:无锁设计(CAS)

private static AtomicReference<Message> sPool = new AtomicReference<>();

public static Message obtain() {
    Message m;
    do {
        m = sPool.get();
        if (m == null) return new Message();
    } while (!sPool.compareAndSet(m, m.next));
    m.next = null;
    return m;
}

方案3:动态大小

// 根据使用情况动态调整
private static int maxPoolSize = 50;

void recycleUnchecked() {
    if (sPoolSize < maxPoolSize) {
        // 放入池
    }
    // 定期统计,调整 maxPoolSize
}

追问:为什么 Android 没有采用这些优化?

  • 答:当前设计已经足够好。消息处理是轻量操作,锁竞争很少。过度优化会增加复杂度,收益不明显。

4.3 实战场景题


【场景题】 应用中发现内存持续增长,怀疑是 Message 对象泄漏,如何排查?

问题分析

  1. 可能原因

    • Handler 持有 Activity 引用导致泄漏
    • 消息中 obj 字段持有大对象
    • 未发送的 Message 没有 recycle
  2. 排查步骤

// 1. 检查 Handler 定义
// 错误:非静态内部类
class MyActivity {
    Handler handler = new Handler() {  // 持有外部类引用
        void handleMessage(Message msg) {}
    };
}

// 正确:静态内部类 + WeakReference
class MyActivity {
    static class MyHandler extends Handler {
        WeakReference<MyActivity> activityRef;
        // ...
    }
}

// 2. 检查 obj 字段
Message msg = Message.obtain();
msg.obj = largeObject;  // 可能导致大对象延迟释放
handler.sendMessageDelayed(msg, 60000);  // 1 分钟后才处理

// 3. 检查未发送的 Message
Message msg = Message.obtain();
if (!shouldSend) {
    // 忘记 recycle
}

解决方案

// 1. 使用静态 Handler
private static class SafeHandler extends Handler {
    private WeakReference<Activity> ref;

    SafeHandler(Activity activity) {
        ref = new WeakReference<>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        Activity activity = ref.get();
        if (activity != null) {
            // 处理消息
        }
    }
}

// 2. 及时清理
@Override
protected void onDestroy() {
    handler.removeCallbacksAndMessages(null);  // 移除所有消息
    super.onDestroy();
}

// 3. 避免 obj 持有大对象
// 使用 what/arg1/arg2 传递简单数据
// 或使用 WeakReference 包装 obj

追问

  • 如何验证 Message 是否泄漏?—— 使用 LeakCanary 或 Android Profiler 的内存分析
  • 对象池本身会导致内存问题吗?—— 不会,MAX_POOL_SIZE=50 限制了大小,且池中对象的 obj 等字段已清空

五、对比与总结

5.1 关键 API 对比

API作用使用场景注意事项
Message.obtain()从池获取 Message所有需要 Message 的场景优先使用
new Message()创建新 Message不推荐性能差
handler.obtainMessage()从池获取并绑定 Handler发送消息便捷方法
msg.recycle()手动回收到池不发送的 Message发送后不要调用
msg.recycleUnchecked()内部回收方法系统使用开发者不要调用

5.2 核心要点速记

一句话记忆: Message 对象池是静态单链表实现的享元模式,obtain() 头部取出,recycle() 头部插入,最大 50 个,线程安全。

3个关键点

  1. 数据结构:静态单链表,复用 next 字段,MAX_POOL_SIZE=50
  2. 操作方式:obtain() 取出/recycle() 插入都是 O(1) 头部操作
  3. 使用原则:始终用 obtain(),发送后不要再操作,让系统自动回收

面试官最爱问

  1. 为什么用 obtain() 而不是 new?(对象复用,减少 GC)
  2. 对象池怎么实现的?(静态单链表,同步锁)
  3. 什么时候回收?(Looper.loop() 处理完自动回收)

六、关联知识点

前置知识

  • Handler 基本概念(详见:../01-Handler基础/
  • 消息入队出队(详见:../03-MessageQueue队列管理/

后续扩展

  • 享元模式在其他场景的应用(RecyclerView.ViewHolder 复用)
  • Android 中的其他对象池(连接池、线程池)
  • 内存优化专题

相关文件

  • ../03-MessageQueue队列管理/01-消息入队enqueueMessage.md - 消息入队时的状态检查
  • ../03-MessageQueue队列管理/02-消息出队next().md - 消息出队后的回收时机
  • ../07-内存泄漏/ - Handler 相关的内存泄漏问题