在 Android 開發中,java.lang.OutOfMemoryError 是最讓開發者頭痛的錯誤之一。特別是當應用程式在處理圖片時突然崩潰,Logcat 吐出一長串堆疊追蹤,指向 BitmapFactory.nativeDecodeStream 或 decodeFile 等方法,這通常意味著記憶體不足以載入該圖片。
從堆疊追蹤可以看出,錯誤發生在 Activity.dispatchActivityResult 階段,說明問題出在從相機或相簿選取圖片返回時。Android 系統為每個應用分配的記憶體有限(早期裝置僅 16MB-24MB),而未經壓縮的高解析度圖片極易超過此限制。當 BitmapFactory 嘗試將圖片解碼載入記憶體時,若可用記憶體不足,便會拋出 OOM 錯誤。
最佳回答指出:「在每張圖片處理完後,對 bitmap 進行回收就 OK 了。」這是解決問題的關鍵。具體實作步驟如下:
public static Bitmap decodeSampledBitmapFromFile(String path, int reqWidth, int reqHeight) {
// 先解碼邊界獲取原始尺寸
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
// 計算縮放比例
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// 真正解碼載入縮放後的圖片
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(path, options);
}
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
if (reqWidth == 0 || reqHeight == 0) return 1;
int inSampleSize = 1;
if (options.outHeight > reqHeight || options.outWidth > reqWidth) {
final int halfHeight = options.outHeight / 2;
final int halfWidth = options.outWidth / 2;
while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
// 使用圖片後
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle(); // 釋放原生記憶體
bitmap = null; // 幫助 GC 回收 Java 物件
System.gc(); // 建議執行垃圾回收(非強制)
}
private LruCache mMemoryCache;
@Override
protected void onCreate(Bundle savedInstanceState) {
// 獲取最大可用記憶體的 1/8 作為快取
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
final int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getByteCount() / 1024; // KB 為單位
}
};
}
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
AsyncTask、ExecutorService 或協程在背景執行緒處理。總結:OOM 錯誤的根本原因是大圖直接載入導致記憶體暴增。透過「縮放載入 + 及時回收 + 記憶體快取」三板斧,配合成熟圖片庫,即可徹底解決此類崩潰問題。