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

Android Bitmap.createBitmap 導致頻繁 GC 的優化實戰

     編輯:程式設計綜合問答

在 onDraw() 中通過 Matrix 變換並調用 Bitmap.createBitmap() 生成新圖片實現動畫,是導致頻繁 Young GC 甚至 OOM 的經典反模式。每幀分配一個 Bitmap 對象,若動畫幀率 60fps,每秒產生 60 個大對象,極大壓力年輕代與大對象區。本文梳理根因並給出三級優化方案。

問題根因分析

  • 對象分配率過高:createBitmap() 會在 Native 堆分配像素內存,並關聯一個 Java Bitmap 對象,雙重壓力。
  • 內存抖動:短命大對象直接進入 Old Gen 或觸發 Concurrent Mark Sweep,導致幀率抖動、卡頓。
  • 未及時回收:若未顯式 recycle()(API 21 前)或依賴 Finalizer,Native 內存釋放滯後。

優化方案(按推薦程度排序)

方案一:Canvas 矩陣變換 + 復用 Bitmap(最優)

不創建新 Bitmap,直接在 onDraw() 中對 Canvas 應用 Matrix:

@Override
protected void onDraw(Canvas canvas) {
    matrix.reset();
    matrix.postRotate(angle, bitmap.getWidth()/2f, bitmap.getHeight()/2f);
    matrix.postTranslate(offsetX, offsetY);
    canvas.drawBitmap(bitmap, matrix, paint); // 單一 Bitmap 復用
    invalidate(); // 請求下一幀
}

優勢:零分配、零 GC、流暢 60fps。
適用:旋轉、縮放、平移等線性變換動畫。

方案二:BitmapPool / LruCache 復用像素內存

若必須生成新 Bitmap(如濾鏡、非線性變形),引入 BitmapPool(Glide/Fresco 內建)或自建 LruCache<Integer, Bitmap> 按尺寸/配置復用:

// 獲取可復用 Bitmap
Bitmap reused = pool.get(width, height, config);
if (reused == null) reused = Bitmap.createBitmap(width, height, config);
// 寫入像素...
canvas.drawBitmap(reused, 0, 0, paint);
// 用完歸還
pool.put(reused);

方案三:降低圖片質量與尺寸(治標)

  • 採樣率 inSampleSize 縮小解碼尺寸。
  • 改用 Bitmap.Config.RGB_565 節省 50% 內存(無透明通道場景)。
  • 僅作為臨時緩解,不可根治分配率問題。

避坑指南

  1. 絕不在 onDraw/循環中 new Bitmap——移至初始化階段或對象池。
  2. API 21+ 依賴 GC,但大對象仍建議 bitmap = null 斷開引用加速回收。
  3. 使用 Profiler / LeakCanary 監控 Native Memory,關注 Graphics 標籤曲線。

Android Studio Profiler 顯示 GC 頻率對比

總結:能用 Canvas 矩陣就別 createBitmap;必須創建時上對象池;質量壓縮只作兜底。這樣能徹底消除動畫幀的 GC 抖動,實現絲滑體驗。

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