Android 中 Bitmap 相关核心技术
Bitmap 是 Android 中表示位图图像的核心类,用于存储图像的像素数据和相关属性,广泛应用于图片加载、显示、编辑等场景。围绕 Bitmap 的技术体系主要涵盖「基础操作、内存优化、图片加载与解码、高效显示、压缩与处理」等核心方向,以下是全面拆解:
一、 Bitmap 基础核心技术
1. Bitmap 实例的创建与销毁
(1) 实例创建的核心方式
Bitmap 无法直接通过 new 实例化,需通过以下 API 构建,核心分为「空位图创建」和「从资源 / 文件 / 流解码创建」:
创建空位图(用于绘制编辑):
// 核心 API:Bitmap.createBitmap() 系列方法 val emptyBitmap = Bitmap.createBitmap( 400, // 宽度(像素) 400, // 高度(像素) Bitmap.Config.ARGB_8888 // 像素配置(关键,影响内存占用) )从资源解码创建(最常用):
val resources = context.resources val bitmapFromRes = BitmapFactory.decodeResource( resources, R.drawable.test, // 资源 ID BitmapFactory.Options() // 解码配置(用于优化) )从文件 / 输入流解码创建:
// 从文件解码 val bitmapFromFile = BitmapFactory.decodeFile("/sdcard/test.jpg") // 从输入流解码(如网络请求返回的流) val inputStream: InputStream = getImageInputStream() val bitmapFromStream = BitmapFactory.decodeStream(inputStream)
(2) 销毁与内存释放
Bitmap 占用内存较大(尤其是高分辨率图片),需及时释放避免内存泄漏 / OOM:
主动销毁:调用
bitmap.recycle(),释放 Bitmap 占用的原生内存(Native 内存),标记位图为「不可用」。辅助回收:将 Bitmap 引用置为
null,帮助 GC 回收 Java 层内存,配合recycle()使用:if (bitmap != null && !bitmap.isRecycled) { bitmap.recycle() // 释放 Native 内存 bitmap = null // 释放 Java 层引用 }注意事项:Android 3.0 以上,Bitmap 的 Native 内存由 ART 虚拟机统一管理,
recycle()不再是必须,但在大量处理图片时,主动调用仍能提升内存利用率。
2. Bitmap 像素配置(Bitmap.Config)
Bitmap.Config 决定了每个像素占用的内存大小,直接影响 Bitmap 的总内存开销(总内存 = 宽度 × 高度 × 每个像素字节数),是内存优化的关键:
| 配置类型 | 每个像素字节数 | 颜色精度 | 适用场景 | 内存占用(示例:400×400 位图) |
|---|---|---|---|---|
ARGB_8888(默认) | 4 字节 | 32 位真彩色(Alpha、Red、Green、Blue 各 8 位) | 对颜色精度要求高的场景(如相册、高清图片显示) | 400×400×4 = 640,000 字节(≈625KB) |
RGB_565 | 2 字节 | 16 位彩色(Red 5 位、Green 6 位、Blue 5 位,无透明通道) | 无透明需求的场景(如普通背景、列表图片) | 400×400×2 = 320,000 字节(≈312KB) |
ARGB_4444 | 2 字节 | 16 位彩色(各通道均 4 位) | 低精度场景(已废弃,颜色失真严重) | 312KB(同 RGB_565) |
ALPHA_8 | 1 字节 | 仅保存透明通道,无颜色信息 | 遮罩、透明效果处理 | 160,000 字节(≈156KB) |
优化建议:无透明需求时,优先使用 RGB_565,可减少 50% 内存占用。
二、 Bitmap 内存优化核心技术
Bitmap 是 Android 中 OOM(内存溢出)的高发源头,核心优化技术围绕「减少内存占用、避免内存泄漏」展开:
1. 解码优化(BitmapFactory.Options)
通过 BitmapFactory.Options 配置解码参数,避免解码高分辨率图片时占用过多内存,核心优化参数如下:
(1) 采样率优化(inSampleSize)
inSampleSize 是「缩放采样率」,表示解码时图片的宽高各缩小为原图的 1/inSampleSize,总内存缩小为 1/(inSampleSize²):
规则:
inSampleSize必须是 ≥1 的整数,若传入非整数,会自动向下取整为 2 的幂(如 3→2、5→4)。使用步骤:
fun decodeSampledBitmapFromRes(resources: Resources, resId: Int, reqWidth: Int, reqHeight: Int): Bitmap { // 步骤 1:设置 inJustDecodeBounds = true,仅获取图片尺寸,不加载像素数据 val options = BitmapFactory.Options().apply { inJustDecodeBounds = true // 不分配内存,仅返回宽高信息 } BitmapFactory.decodeResource(resources, resId, options) // 此时返回 null,仅填充 options 信息 // 步骤 2:计算合适的 inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight) // 步骤 3:设置 inJustDecodeBounds = false,解码缩放后的图片 options.inJustDecodeBounds = false return BitmapFactory.decodeResource(resources, resId, options) } // 计算最优 inSampleSize fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int { val (width: Int, height: Int) = options.outWidth to options.outHeight var inSampleSize = 1 if (height > reqHeight || width > reqWidth) { val halfHeight = height / 2 val halfWidth = width / 2 // 找到最大的 inSampleSize(2 的幂),使得缩放后的宽高仍大于需求宽高 while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { inSampleSize *= 2 } } return inSampleSize }
(2) 其他关键优化参数
inPreferredConfig:指定解码后的像素配置(如Bitmap.Config.RGB_565),减少内存占用。inScaled:是否允许缩放(默认true),配合inDensity(原图密度)、inTargetDensity(目标密度)实现按密度缩放。inPurgeable/inInputShareable:Android 低版本(❤️.0)用于允许 Bitmap 内存被系统回收,高版本已失效。
2. 内存缓存技术(LruCache)
利用「最近最少使用(LRU)」缓存策略,缓存已解码的 Bitmap,避免重复解码消耗内存和 CPU,核心使用 LruCache 类(Android 提供的 LRU 缓存实现):
class BitmapLruCache(context: Context) {
// 步骤 1:计算可用缓存内存(一般取应用最大可用内存的 1/8)
private val maxMemory = (Runtime.getRuntime().maxMemory() / 1024).toInt()
private val cacheSize = maxMemory / 8
// 步骤 2:初始化 LruCache,指定缓存大小,重写 sizeOf 方法计算每个 Bitmap 的大小
private val bitmapLruCache = object : LruCache<String, Bitmap>(cacheSize) {
override fun sizeOf(key: String, bitmap: Bitmap): Int {
// 返回 Bitmap 占用的内存大小(单位:KB)
return bitmap.byteCount / 1024
}
}
// 缓存 Bitmap
fun putBitmap(key: String, bitmap: Bitmap) {
if (getBitmap(key) == null) {
bitmapLruCache.put(key, bitmap)
}
}
// 获取缓存的 Bitmap
fun getBitmap(key: String): Bitmap? {
return bitmapLruCache.get(key)
}
// 移除指定缓存
fun removeBitmap(key: String) {
bitmapLruCache.remove(key)
}
// 清空缓存
fun clearCache() {
bitmapLruCache.evictAll()
}
}
3. 磁盘缓存技术(DiskLruCache)
配合内存缓存,将不常用的 Bitmap 缓存到磁盘(SD 卡),避免内存缓存被回收后重复从网络 / 资源解码,核心使用 DiskLruCache(Android 推荐的磁盘 LRU 缓存实现,需手动引入或自定义):
- 核心作用:弥补内存缓存的不足(内存缓存有限,应用退到后台可能被系统回收)。
- 适用场景:网络图片加载(如朋友圈、电商商品图片),缓存下载后的图片到本地。
- 注意事项:磁盘读写耗时,需在子线程执行,避免阻塞主线程。
4. 避免 Bitmap 内存泄漏
- 避免静态引用持有 Bitmap(静态引用生命周期与应用一致,无法被 GC 回收)。
- Activity/Fragment 销毁时,及时回收 Bitmap(
recycle()+ 置为null),清空缓存。 - 列表(RecyclerView/ListView)中使用 Bitmap 时,复用 ViewHolder,避免创建过多 Bitmap 实例,且滑动时暂停图片加载。
三、 高效图片加载框架(基于 Bitmap 封装)
实际开发中,很少直接手动操作 Bitmap(繁琐且易出问题),更多使用成熟的图片加载框架,它们已封装了 Bitmap 的解码、缓存、优化等逻辑:
1. Glide(推荐,Google 官方推荐)
核心优势:自动适配 Bitmap 内存优化、支持生命周期绑定(避免内存泄漏)、自动压缩、支持 GIF / 视频帧加载。
简单使用:
Glide.with(context) .load("https://test.com/test.jpg") // 图片资源(网络、本地、资源 ID) .placeholder(R.drawable.placeholder) // 占位图 .error(R.drawable.error) // 错误图 .override(400, 400) // 指定显示尺寸 .into(imageView) // 显示到 ImageView
2. Picasso(Square 出品,简洁轻量)
核心优势:API 简洁、自动内存 / 磁盘缓存、自动适配 ImageView 尺寸。
简单使用:
Picasso.get() .load("https://test.com/test.jpg") .placeholder(R.drawable.placeholder) .error(R.drawable.error) .resize(400, 400) .into(imageView)
3. Fresco(Facebook 出品,高内存优化)
- 核心优势:Bitmap 内存存储在 Native 层,避免 Java 层 OOM、支持渐进式加载、适合超大图片。
- 适用场景:对内存要求极高的应用(如相册、图片编辑类 App)。
四、 Bitmap 高级处理技术
1. Bitmap 裁剪与缩放
裁剪:使用
Bitmap.createBitmap()裁剪指定区域的像素:// 裁剪原图的 (100,100) 到 (300,300) 区域 val croppedBitmap = Bitmap.createBitmap( originalBitmap, 100, // 起始 X 坐标 100, // 起始 Y 坐标 200, // 裁剪宽度 200 // 裁剪高度 )缩放:使用
Matrix配合Bitmap.createBitmap()实现任意比例缩放:fun scaleBitmap(originalBitmap: Bitmap, scale: Float): Bitmap { val matrix = Matrix().apply { postScale(scale, scale) // x、y 轴缩放比例 } return Bitmap.createBitmap( originalBitmap, 0, 0, originalBitmap.width, originalBitmap.height, matrix, true // 是否过滤(抗锯齿,提升缩放后画质) ) }
2. Bitmap 压缩(质量压缩 + 尺寸压缩)
(1) 质量压缩(compress())
核心:降低图片的压缩比,减少文件大小,不改变 Bitmap 的像素尺寸和内存占用,仅影响输出的文件(如 JPG/PNG)大小。
适用场景:图片上传(减少网络传输大小)。
fun compressBitmapQuality(bitmap: Bitmap, outputPath: String): Boolean { val outputStream = FileOutputStream(outputPath) // 压缩格式:JPEG(支持有损压缩)、PNG(无损压缩,压缩无效) val result = bitmap.compress( Bitmap.CompressFormat.JPEG, 80, // 压缩质量(0-100,100 为无压缩) outputStream ) outputStream.close() return result }
(2) 尺寸压缩
- 核心:通过缩小 Bitmap 的宽高,减少内存占用和文件大小(即前文的「采样率优化」)。
- 适用场景:图片显示(如列表缩略图),既减少内存占用,也减少文件存储大小。
3. Bitmap 绘制与合成
通过 Canvas 将多个 Bitmap 合成,或在 Bitmap 上绘制文字、图形:
fun composeBitmap(bitmap1: Bitmap, bitmap2: Bitmap): Bitmap {
// 创建空白位图作为画布
val composeBitmap = Bitmap.createBitmap(
bitmap1.width,
bitmap1.height,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(composeBitmap)
// 绘制第一张位图(底层)
canvas.drawBitmap(bitmap1, 0f, 0f, null)
// 绘制第二张位图(上层,偏移 (50,50))
canvas.drawBitmap(bitmap2, 50f, 50f, null)
// 可选:绘制文字
val paint = Paint().apply {
textSize = 30f
color = Color.WHITE
}
canvas.drawText("合成图片", 100f, 100f, paint)
return composeBitmap
}
五、 关键注意事项与最佳实践
- 避免在主线程处理 Bitmap:解码、压缩、合成等操作耗时,应在子线程(如
AsyncTask、Coroutine)执行,避免卡顿。 - 适配不同分辨率设备:通过
DisplayMetrics获取设备屏幕密度,合理缩放 Bitmap,避免图片拉伸 / 模糊。 - 监控 Bitmap 内存:使用 Android Studio Profiler 的「Memory」面板,监控 Bitmap 内存占用,排查 OOM 问题。
- 高版本适配:Android 8.0 以上限制后台进程访问 SD 卡,Bitmap 磁盘缓存需适配「分区存储」。
- 优先使用矢量图(VectorDrawable):对于简单图标,使用 VectorDrawable 替代 Bitmap,可自适应各种分辨率,且占用内存极小。
总结
Android 中 Bitmap 相关技术的核心逻辑是「高效加载、优化内存、安全使用」:
- 基础层:掌握 Bitmap 的创建、销毁、像素配置,是后续优化的基础。
- 优化层:通过解码采样、LruCache 缓存、避免内存泄漏,解决 OOM 问题。
- 应用层:使用 Glide/Picasso 等框架,简化 Bitmap 操作,提升开发效率。
- 高级层:掌握 Bitmap 裁剪、压缩、合成,满足复杂图片处理场景。