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

Android Byte[] 轉 Bitmap 圖片變花/重疊問題:旋轉矩陣導致的記憶體殘留與正確處理流程

     編輯:程式設計綜合問答

相機預覽或拍照後,BitmapFactory.decodeByteArray(data, 0, data.length) 解碼出的 Bitmap 若直接顯示正常,但經 Matrix.postRotate(90) 旋轉後出現「多張圖片重疊、色彩錯亂、綠色噪點」等現象,通常是**旋轉邏輯建立了新 Bitmap 卻未釋放原 Bitmap**,或**解碼配置(Config)與旋轉不匹配**導致。

問題現場重現


// 錯誤範例
byte[] data = ...; // 相機回調 data
Bitmap b = BitmapFactory.decodeByteArray(data, 0, data.length); // 原始 Bitmap
Matrix matrix = new Matrix();
matrix.postRotate(90);
Bitmap rotated = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), matrix, false);
// 此時 rotated 顯示異常:重疊、花屏
imageView.setImageBitmap(rotated);

根本原因分析

  1. 原始 Bitmap 未回收:createBitmap 會**複製像素資料**建立新 Bitmap,原 b 若不 recycle(),記憶體佔用翻倍,且某些裝置驅動會在同一記憶體區域殘留舊像素,造成視覺重疊。
  2. 解碼 Config 不支援旋轉:部分裝置相機輸出 YUV_420_888 或 NV21,decodeByteArray 預設產生 ARGB_8888,但旋轉後若硬體加速畫布期望 RGB_565,會出現色彩錯亂。
  3. Matrix 旋轉中心點錯誤:postRotate(90) 以 (0,0) 為中心,導致圖片平移出可視區,需配合 preTranslate 或使用 setRotate(degrees, px, py) 指定中心。

正確處理流程(三步驟)

步驟 1:解碼時指定 Config 並獲取 EXIF 方向


BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inPreferredConfig = Bitmap.Config.ARGB_8888; // 統一格式
opts.inMutable = true; // 允許後續修改(如旋轉)
Bitmap src = BitmapFactory.decodeByteArray(data, 0, data.length, opts);

// 讀取 EXIF 方向(相機拍照通常需旋轉 90/180/270)
ExifInterface exif = new ExifInterface(new ByteArrayInputStream(data)); // API 24+
// 或舊 API: ExifInterface exif = new ExifInterface(filePath);
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
int rotation = 0;
switch (orientation) {
    case ExifInterface.ORIENTATION_ROTATE_90:  rotation = 90; break;
    case ExifInterface.ORIENTATION_ROTATE_180: rotation = 180; break;
    case ExifInterface.ORIENTATION_ROTATE_270: rotation = 270; break;
}

步驟 2:正確旋轉(指定中心點、復用 Bitmap)


if (rotation != 0) {
    Matrix matrix = new Matrix();
    // 以圖片中心為旋轉中心,避免平移
    matrix.setRotate(rotation, src.getWidth() / 2f, src.getHeight() / 2f);
    // 嘗試原地旋轉(API 19+),失敗則建立新 Bitmap
    Bitmap rotated = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);
    if (rotated != src) {
        src.recycle(); // 釋放原 Bitmap 記憶體
        src = rotated;
    }
}

步驟 3:儲存/顯示前確保像素已就緒


// 強制像素複製到 Java 堆(避免硬體加速畫布直接讀取 native 記憶體導致花屏)
src.prepareToDraw(); // API 17+
imageView.setImageBitmap(src);
// 或儲存檔案
try (FileOutputStream fos = new FileOutputStream(outFile)) {
    src.compress(Bitmap.CompressFormat.JPEG, 90, fos);
}

進階優化:使用 ImageDecoder (API 28+)


ImageDecoder.Source source = ImageDecoder.createSource(new ByteArrayInputStream(data));
Bitmap bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, source) -> {
    decoder.setTargetSize(info.getSize()); // 可選縮放
    decoder.setAllocator(Allocator.HARDWARE); // 硬體加速
});
// ImageDecoder 內建 EXIF 方向自動修正,無需手動旋轉!

常見坑點檢查清單

  • ✅ decodeByteArray 設定 inMutable=true 且指定 Config。
  • ✅ 讀取 EXIF 方向決定旋轉角度,**不要寫死 90 度**。
  • ✅ 旋轉用 setRotate(deg, cx, cy) 指定中心點。
  • ✅ createBitmap 回傳新物件時,**必須 recycle() 舊物件**。
  • ✅ 顯示前呼叫 prepareToDraw() 確保像素就緒。
  • ✅ 長時間持有 Bitmap 時,在 onDestroy 統一 recycle()。

總結

Byte[] 轉 Bitmap 變花本質是**記憶體管理與座標變換細節疏漏**。遵循「解碼指定 Config → 讀 EXIF 定旋轉 → 中心點旋轉 → 釋放舊 Bitmap → prepareToDraw」標準流程,配合 API 28+ ImageDecoder 自動處理 EXIF,即可徹底解決旋轉重疊、色彩異常問題,打造穩健的相機應用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved