一句话说透Android里面的Bitmap使用时应该注意什么

一句话总结:

用 Bitmap 就像搬砖------图片太大直接扛,内存分分钟爆缸!得学会"巧裁切、会缓存、勤回收",否则 App 卡崩没商量!


一、内存暴增,原地爆炸(OOM)

问题: 一张照片原图 4000x3000 像素 → ARGB_8888 格式占内存 48MB(4000x3000x4字节)!直接加载?低端机当场崩溃!

保命操作1:压缩到控件大小

kotlin 复制代码
// 先获取图片尺寸,不加载内存  
val options = BitmapFactory.Options().apply {  
    inJustDecodeBounds = true  
}  
BitmapFactory.decodeFile(path, options)  
val (width, height) = options.outWidth to options.outHeight  

// 计算缩放比例(目标控件大小 200x200)  
val inSampleSize = calculateInSampleSize(options, 200, 200)  

// 真正加载压缩后的图片  
options.inJustDecodeBounds = false  
options.inSampleSize = inSampleSize  
val bitmap = BitmapFactory.decodeFile(path, options) // 内存降到 200x200x4 ≈ 0.16MB!  

// 计算缩放比例的方法  
fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {  
    val (width, height) = options.outWidth to options.outHeight  
    var inSampleSize = 1  
    if (height > reqHeight || width > reqWidth) {  
        val halfHeight = height / 2  
        val halfWidth = width / 2  
        while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {  
            inSampleSize *= 2  
        }  
    }  
    return inSampleSize  
}  

保命操作2:用低内存格式(有损画质)

ini 复制代码
options.inPreferredConfig = Bitmap.Config.RGB_565 // 每个像素占2字节(省一半内存!)  

二、不回收Bitmap,内存泄漏!

案例: 在 Activity 中加载 Bitmap 没回收 → Activity 销毁后 Bitmap 还在内存 → 反复打开关闭 → 内存泄漏!

解决:

  • Bitmap.recycle()(谨慎使用!):

    kotlin 复制代码
    override fun onDestroy() {  
        bitmap?.recycle() // 手动回收(注意:回收后不能再使用!)  
        bitmap = null  
        super.onDestroy()  
    }  
  • LruCacheGlide 自动管理:

    kotlin 复制代码
    // LruCache 示例(最大内存的1/8)  
    val maxMemory = (Runtime.getRuntime().maxMemory() / 1024).toInt()  
    val cacheSize = maxMemory / 8  
    val bitmapCache = object : LruCache<String, Bitmap>(cacheSize) {  
        override fun sizeOf(key: String, bitmap: Bitmap): Int {  
            return bitmap.byteCount / 1024  
        }  
    }  

三、主线程加载大图,ANR 警告!

作死代码:

ini 复制代码
button.setOnClickListener {  
    // 直接在主线程加载大图 → 卡死5秒 → ANR!  
    val bitmap = BitmapFactory.decodeFile("sdcard/big_image.jpg")  
    imageView.setImageBitmap(bitmap)  
}  

保命操作:子线程加载 + 切主线程显示

scss 复制代码
// 用协程(Kotlin推荐)  
lifecycleScope.launch(Dispatchers.IO) {  
    val bitmap = loadBitmapFromFile("sdcard/big_image.jpg")  
    withContext(Dispatchers.Main) {  
        imageView.setImageBitmap(bitmap)  
    }  
}  

四、重复创建Bitmap,内存抖动!

案例: ListView/RecyclerView 的 getView() 里频繁 new Bitmap → GC 疯狂回收 → UI 卡顿!

解决: 用内存缓存 + 磁盘缓存 + 复用 ConvertView

kotlin 复制代码
// RecyclerView.ViewHolder 中复用 Bitmap  
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {  
    private var currentBitmap: Bitmap? = null  

    fun bind(imagePath: String) {  
        // 异步加载并复用 Bitmap  
        loadBitmapAsync(imagePath) { bitmap ->  
            currentBitmap?.recycle() // 释放旧的  
            currentBitmap = bitmap  
            itemView.imageView.setImageBitmap(bitmap)  
        }  
    }  

    fun clear() {  
        currentBitmap?.recycle()  
    }  
}  

五、终极保命指南

  1. 能缩则缩: 图片尺寸对齐控件大小,别加载原图!
  2. 能用缓存就别新建: LruCache、DiskLruCache 搞起来!
  3. 能异步就别阻塞主线程: 协程、RxJava、AsyncTask 任选!
  4. 能复用就别回收: ViewHolder 复用 Bitmap,减少GC压力!
  5. 能交给库就别自己写: Glide、Picasso 它不香吗?

口诀:
"大图不压必崩,复用缓存保命,
主线程里别硬刚,Glide一甩全搞定!"

(附:第三方库 Glide 的终极偷懒写法👇)

scss 复制代码
Glide.with(context)  
    .load("http://xxx.jpg")  
    .override(200, 200) // 强制缩放  
    .format(DecodeFormat.PREFER_RGB_565) // 低内存格式  
    .into(imageView)  
相关推荐
自由生长20241 小时前
JNI是什么?
程序员
贺biubiu4 小时前
2025 年终总结|总有那么一个人,会让你千里奔赴...
android·程序员·年终总结
xuekai200809014 小时前
mysql-组复制 -8.4.7 主从搭建
android·adb
野生的码农4 小时前
做好自己的份内工作,等着被裁
程序员·ai编程·vibecoding
nono牛5 小时前
ps -A|grep gate
android
Jing_Rainbow6 小时前
【 前端三剑客-37 /Lesson61(2025-12-09)】JavaScript 内存机制与执行原理详解🧠
前端·javascript·程序员
未知名Android用户6 小时前
Android动态变化渐变背景
android
nono牛7 小时前
Gatekeeper 的精确定义
android
stevenzqzq9 小时前
android启动初始化和注入理解3
android
城东米粉儿11 小时前
compose 状态提升 笔记
android