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 對享元模式的最佳實踐:內部狀態緩存復用,外部狀態調用方提供,既省內存又免鎖。