Java中最常用的设计模式-CSDN博客
把“不可变且可复用”的细粒度对象缓存起来,用“共享”代替“新建”,从而节省内存。
经典场景
-
字符串常量池、
Integer.valueOf(-128~127)
、AndroidMessage.obtain()
-
游戏粒子、编辑器字形、地图瓦片、线程池中的任务包装器
android Message
Android 里 Message
对享元模式(Flyweight)的实现就是 “对象池 + 不可变字段拆分” 的典型案例。
Android 的 Message
把享元模式做成了 “运行期对象池”:
内部状态(what/arg1/arg2/obj 等字段结构)被所有消息共享;外部状态(具体数值、target、when)由调用者每次临时传入;用完调用 recycle()
把对象回收到 静态链表池(sPool),下次 obtain()
再取出来复用,从而避免大量 new Message()
造成的内存与 GC 压力
核心流程
-
借对象:
Message.obtain()
从链表头 sPool 弹出一个空闲节点,池空才new Message()
。 -
填充外部状态:调用者给
what
、arg1
、obj
等赋值。 -
回收对象:Handler 处理完消息后自动走
recycle()
→recycleUnchecked()
,把字段清零并插回链表,池大小上限 50
同步与性能
-
使用 sPoolSync 对象锁 保证多线程安全;
-
链表替代 Map,减少哈希计算,O(1) 取出/归还;
-
只缓存“空壳”字段结构,不缓存业务数据,实现 零状态污染 的共享
Message.obtain()
就是 Android 对享元模式的落地:把不变字段当模板共享,把变化数据当参数注入,用链表池循环复用,既省内存又保线程安全
享元池核心源码
// 系统隐藏类:android.os.Message
public final class Message implements Parcelable {/* 享元字段:可复用的内部状态 */public int what;public int arg1;public int arg2;public Object obj;/* 外部状态:Handler、时间戳等由调用者每次传入 *//* ... */// ===== 享元工厂 =====private static final Object sPoolSync = new Object();private static Message sPool; // 链表头private static int sPoolSize = 0;private static final int MAX_POOL_SIZE = 50;// 对外暴露的“借对象”方法public static Message obtain() {synchronized (sPoolSync) {if (sPool != null) {Message m = sPool;sPool = m.next;m.next = null;sPoolSize--;return m;}}return new Message(); // 池空就新建}// 用完归还public void recycle() {// 清空可复用字段what = 0; arg1 = 0; arg2 = 0; obj = null;synchronized (sPoolSync) {if (sPoolSize < MAX_POOL_SIZE) {next = sPool;sPool = this;sPoolSize++;}}}
}
客户端使用示例
// 借
Message msg = Message.obtain(); // 复用池内对象
msg.what = 1;
msg.arg1 = 100;
msg.obj = "Hello";// 发送
handler.sendMessage(msg);// 系统 Looper 处理完后会自动调 recycle() 归还
Message.obtain()
就是 Android 对享元模式的最佳实践:内部状态缓存复用,外部状态调用方提供,既省内存又免锁。