在 onDraw() 中通過 Matrix 變換並調用 Bitmap.createBitmap() 生成新圖片實現動畫,是導致頻繁 Young GC 甚至 OOM 的經典反模式。每幀分配一個 Bitmap 對象,若動畫幀率 60fps,每秒產生 60 個大對象,極大壓力年輕代與大對象區。本文梳理根因並給出三級優化方案。
createBitmap() 會在 Native 堆分配像素內存,並關聯一個 Java Bitmap 對象,雙重壓力。recycle()(API 21 前)或依賴 Finalizer,Native 內存釋放滯後。不創建新 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。
適用:旋轉、縮放、平移等線性變換動畫。
若必須生成新 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% 內存(無透明通道場景)。bitmap = null 斷開引用加速回收。Graphics 標籤曲線。
總結:能用 Canvas 矩陣就別 createBitmap;必須創建時上對象池;質量壓縮只作兜底。這樣能徹底消除動畫幀的 GC 抖動,實現絲滑體驗。