首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Android Activity 間傳遞 Bitmap 陣列:靜態持有者模式優於 Intent 的最佳實務

     編輯:程式設計綜合問答

在 Android 中嘗試透過 Intent.putExtra() 傳遞 Bitmap[] 或 ArrayList<Bitmap> 會立即遭遇 TransactionTooLargeException(Binder 緩衝區限制 1MB),或因序列化/反序列化導致嚴重效能下降與 OOM。業界標準解法:**同一應用進程內,使用靜態持有者或單例模式共享記憶體引用**。

為何 Intent 不適合傳遞 Bitmap 陣列?

推薦方案:靜態持有者模式


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。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
限制影響
Binder 緩衝區 1MB (共享全系統)超過即拋出 TransactionTooLargeException,App 崩潰
Parcelable 序列化需複製像素資料記憶體暴增 2~3 倍,極易 OOM
跨進程需序列化,同進程仍走 Binder即使同一 App,Activity 間傳遞也受 1MB 限制