跳到主要内容

Bitmap 内存优化

问题

Bitmap 为什么占用内存大?如何优化 Bitmap 的内存使用?

答案

Bitmap 内存计算

内存大小 = 宽 × 高 × 每像素字节数
格式每像素字节说明
ARGB_88884默认,最高质量
RGB_5652无透明通道,省一半
ARGB_44442已废弃
HARDWARE-GPU 纹理,不占 Java 堆

一张 4000×3000 的照片:4000 × 3000 × 4 = 48MB

采样压缩(inSampleSize)

fun decodeSampledBitmap(res: Resources, resId: Int, reqWidth: Int, reqHeight: Int): Bitmap {
val options = BitmapFactory.Options().apply {
// 第一步:只读尺寸,不加载像素
inJustDecodeBounds = true
}
BitmapFactory.decodeResource(res, resId, options)

// 第二步:计算采样率
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight)

// 第三步:按采样率加载
options.inJustDecodeBounds = false
return BitmapFactory.decodeResource(res, resId, options)
}

fun calculateInSampleSize(options: BitmapFactory.Options, reqW: Int, reqH: Int): Int {
val (height, width) = options.outHeight to options.outWidth
var inSampleSize = 1
if (height > reqH || width > reqW) {
val halfH = height / 2
val halfW = width / 2
while (halfH / inSampleSize >= reqH && halfW / inSampleSize >= reqW) {
inSampleSize *= 2 // 只能是 2 的整数次幂
}
}
return inSampleSize
}

Bitmap 复用(inBitmap)

val options = BitmapFactory.Options().apply {
inMutable = true
inBitmap = reusableBitmap // 复用已有 Bitmap 的内存
}
val bitmap = BitmapFactory.decodeStream(stream, null, options)
inBitmap 限制
  • Android 4.4 以前:新 Bitmap 必须与被复用 Bitmap 大小完全一致
  • Android 4.4+:被复用 Bitmap 大小 >= 新 Bitmap 即可

图片加载库的优化策略

Glide / Coil 内部已做了以下优化:

  1. 自动降采样:根据 ImageView 大小加载合适尺寸
  2. 内存缓存 + 磁盘缓存:LRU 缓存
  3. Bitmap 回收池:BitmapPool 复用
  4. 请求生命周期管理:页面销毁取消加载

常见面试问题

Q1: 如何加载超大图片(比如长图)?

答案

使用 BitmapRegionDecoder 按区域解码,配合自定义 View 实现滑动查看:

val decoder = BitmapRegionDecoder.newInstance(inputStream, false)
val rect = Rect(0, 0, screenWidth, screenHeight) // 只解码可见区域
val bitmap = decoder.decodeRegion(rect, BitmapFactory.Options())

Q2: RGB_565 什么时候使用?

答案

当图片不需要透明通道时(如照片、纯色背景图),可使用 RGB_565 节省 50% 内存。但对于 PNG 图片(含透明区域),必须使用 ARGB_8888。

Glide 默认使用 RGB_565(不含透明),Coil 默认使用 ARGB_8888。

相关链接