一句话说透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)  
相关推荐
2501_9160137411 小时前
iOS混淆工具有哪些?跨平台 App 混淆与保护的实用方案
android·ios·小程序·https·uni-app·iphone·webview
2501_9159090611 小时前
iOS 文件管理实战指南,用户文件、安全访问与开发调试方案
android·ios·小程序·https·uni-app·iphone·webview
摆烂工程师14 小时前
教你如何从GPT-5 切换到 GPT-4o。Plus 用户切换 GPT-4o 旧模型的入口在哪里?
人工智能·chatgpt·程序员
没有了遇见15 小时前
Android虚拟机与虚拟空间检测实战详解<二>
android
峥嵘life15 小时前
Android初学者系统开发学习路线参考
android·学习
Xu_youyaxianshen17 小时前
Android 缓存日志(Logcat)导出与分析全攻略
android·缓存·log日志
黑白小道士17 小时前
Kotlin 中,run、also、let、apply、with 是常用的作用域函数
android·kotlin
摆烂工程师18 小时前
GPT-5 对应用户可以使用的次数,以及解决 GPT-5 没有推送的问题
人工智能·gpt·程序员
0wioiw020 小时前
Android-Kotlin基础(Jetpack③-LiveData)
android·开发语言·kotlin
xzkyd outpaper20 小时前
Android中Binder缓冲区为什么限制1MB,此外Bundle数据为什么要存储在Binder缓冲区中
android·binder