一句话说透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()  
    }  
  • 用 LruCache 或 Glide 自动管理:

    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)  
相关推荐
如约见5 小时前
技术岗的「本科及以上」,多是软门槛
程序员·学历·技术岗
陈随易7 小时前
AI 运维服务器,安全又可靠的方法
前端·后端·程序员
三少爷的鞋8 小时前
Java 线程自上而下,Kotlin 协程自下而上
android
青衫嵌入式8 小时前
根据 STM32 逐行拆解 FreeRTOS 的任务上下文切换
程序员
好的佩奇9 小时前
Dart 之增强枚举
android·flutter·dart
梦影_10 小时前
MySQL进阶教程(五)—— SQL调优(下)
android·大数据·数据库·后端·sql·mysql·database
好的佩奇12 小时前
蓝牙之无线电与通信距离
android·flutter·蓝牙
vilya12 小时前
我把一套完整的 Python Agent 运行时塞进手机 APK:依赖闭包、Rust 轮子和三个真机坑
android
呆呆敲代码的小Y14 小时前
【实战精选面试题】C# 面试题详细解答,按难易程度排序
android·java·游戏·c#·游戏开发·unity面试题·c#面试题
思想觉悟16 小时前
SEAndroid实战
android·linux·c++