相機預覽或拍照後,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);
createBitmap 會**複製像素資料**建立新 Bitmap,原 b 若不 recycle(),記憶體佔用翻倍,且某些裝置驅動會在同一記憶體區域殘留舊像素,造成視覺重疊。YUV_420_888 或 NV21,decodeByteArray 預設產生 ARGB_8888,但旋轉後若硬體加速畫布期望 RGB_565,會出現色彩錯亂。postRotate(90) 以 (0,0) 為中心,導致圖片平移出可視區,需配合 preTranslate 或使用 setRotate(degrees, px, py) 指定中心。
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;
}
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;
}
}
// 強制像素複製到 Java 堆(避免硬體加速畫布直接讀取 native 記憶體導致花屏)
src.prepareToDraw(); // API 17+
imageView.setImageBitmap(src);
// 或儲存檔案
try (FileOutputStream fos = new FileOutputStream(outFile)) {
src.compress(Bitmap.CompressFormat.JPEG, 90, fos);
}
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。setRotate(deg, cx, cy) 指定中心點。createBitmap 回傳新物件時,**必須 recycle() 舊物件**。prepareToDraw() 確保像素就緒。onDestroy 統一 recycle()。Byte[] 轉 Bitmap 變花本質是**記憶體管理與座標變換細節疏漏**。遵循「解碼指定 Config → 讀 EXIF 定旋轉 → 中心點旋轉 → 釋放舊 Bitmap → prepareToDraw」標準流程,配合 API 28+ ImageDecoder 自動處理 EXIF,即可徹底解決旋轉重疊、色彩異常問題,打造穩健的相機應用。