| 限制 | 影響 |
| Binder 緩衝區 1MB (共享全系統) | 超過即拋出 TransactionTooLargeException,App 崩潰 |
| Parcelable 序列化需複製像素資料 | 記憶體暴增 2~3 倍,極易 OOM |
| 跨進程需序列化,同進程仍走 Binder | 即使同一 App,Activity 間傳遞也受 1MB 限制 |
推薦方案:靜態持有者模式
public final class BitmapHolder {
private static BitmapHolder INSTANCE;
private Bitmap[] bitmaps;
private int referenceCount = 0;
private BitmapHolder() {}
public static synchronized BitmapHolder getInstance() {
if (INSTANCE == null) INSTANCE = new BitmapHolder();
return INSTANCE;
}
// 設定資料,返回 token 用於後續取回並釋放
public synchronized int setBitmaps(Bitmap[] bitmaps) {
this.bitmaps = bitmaps;
return ++referenceCount; // 簡單 token
}
public synchronized Bitmap[] getBitmaps(int token) {
if (token == referenceCount) {
Bitmap[] result = bitmaps;
bitmaps = null; // 取走即清空,避免洩漏
referenceCount = 0;
return result;
}
return null; // token 失效
}
public synchronized void clear() {
bitmaps = null;
referenceCount = 0;
}
}
使用範例
// Activity A:準備資料並啟動 B
Bitmap[] bitmaps = loadBitmaps();
int token = BitmapHolder.getInstance().setBitmaps(bitmaps);
Intent intent = new Intent(this, ActivityB.class);
intent.putExtra("bitmap_token", token);
startActivity(intent);
// Activity B:取回資料
int token = getIntent().getIntExtra("bitmap_token", -1);
Bitmap[] bitmaps = BitmapHolder.getInstance().getBitmaps(token);
if (bitmaps != null) {
// 使用 bitmaps
// 使用完畢後可選擇 recycle()
}
進階替代方案對比
| 方案 | 適用場景 | 優點 | 缺點 |
|------|----------|------|------|
| **靜態持有者** | 同進程 Activity/Fragment 間傳遞大型物件 | 零複製、零序列化、效能最高 | 需手動管理生命週期,防洩漏 |
| ViewModel (共享) | Fragment 與 Activity、Fragment 間共享 | 生命週期感知、配置變更倖存、Google 推薦 | 僅限同一 Activity 範圍內共享 |
| Application 子類持有 | 全域單例資料 (如用戶頭像緩存) | 存取簡單、全程可用 | 易造成記憶體洩漏,需弱引用/LruCache |
| 檔案/磁碟快取 (DiskLruCache) | 跨進程、App 重啟後仍需資料 | 持久化、不佔記憶體 | 讀寫較慢、需序列化 |
| ContentProvider | 跨 App 安全共享圖片 | 標準跨進程機制、權限控制 | 實作複雜、有 Binder 限制 |
記憶體洩漏防護清單
- ✅ 取回資料後**立即清空持有者** (
getBitmaps 內部設 null)。
- ✅ 若 Bitmap 極大,使用
WeakReference<Bitmap[]> 持有,配合 LruCache。
- ✅ 在
Application.onTrimMemory / onLowMemory 中主動清理。
- ✅ 使用
LeakCanary 偵測靜態欄位洩漏。
總結
Android 組件間傳遞 Bitmap[],**絕不可用 Intent**。同進程場景首選**靜態持有者/單例模式**零複製共享引用;架構化專案請使用 **Shared ViewModel**;跨進程/持久化需求轉向 **DiskLruCache** 或 **ContentProvider**。嚴守「大物件不進 Bundle」鐵律,杜絕 TransactionTooLargeException 與 OOM。
- 上一篇文章: C 語言聯合體/指標轉換陷阱:為何浮點數以 %d 輸出為 0 的記憶體視角解析
- 下一篇文章: Java Socket 多線程雙向通訊實戰:伺服器端持續監聽、客戶端專屬執行緒處理多輪讀寫
Copyright ©
程式碼之家 All Rights Reserved