11、图片优化:图片格式选择、Glide与Coil对比、图片压缩策略、三级缓存原理
图片优化,说白了就是跟内存和流量较劲。我做了这么多年Android性能优化,最头疼的就是图片。一张大图没处理好,直接OOM,用户骂娘,你还得背锅。今天咱们就把图片优化这块掰开揉碎了讲清楚。
一、图片格式怎么选?WebP还是AVIF?
先聊聊格式。我个人习惯,能上WebP就上WebP。为什么?因为它在Android生态里支持得最好,压缩率比PNG高30%左右,而且支持透明通道。我在项目中遇到过,一套UI切图从PNG换成WebP,包体积直接少了40%。
| 格式 | 压缩率 | 透明通道 | Android支持 | 适用场景 |
|---|---|---|---|---|
| PNG | 低 | 支持 | 原生 | 需要无损、透明图 |
| JPEG | 中 | 不支持 | 原生 | 照片、大背景图 |
| WebP | 高 | 支持 | Android 4.0+ | 推荐首选 |
| AVIF | 极高 | 支持 | Android 12+ | 高端机型、新项目 |
AVIF是后起之秀,压缩率比WebP再高20%左右。但注意,它需要Android 12以上才原生支持。你想想看,如果你的App还要兼容老机型,用AVIF就得自己解码,那性能反而得不偿失。我建议,新项目可以上AVIF,老项目老老实实用WebP。
二、Glide vs Coil:到底选哪个?
这两个库我都深度用过。Glide是老牌劲旅,Coil是后起之秀。说实话,没有绝对的好坏,看你的场景。
Glide的优势
- 生态成熟,踩坑的人多,网上资料丰富
- 支持GIF、视频帧、自定义解码器
- 内存管理做得不错,有三级缓存
Coil的优势
- 纯Kotlin实现,协程原生支持
- 包体积小,只有Glide的1/3左右
- API更简洁,链式调用很舒服
我记得有一次,项目里图片加载特别频繁,Glide的Bitmap池偶尔会抖动。换成Coil后,因为它的内存管理基于Kotlin协程,反而更稳定。但如果你项目里还有大量Java代码,Glide会更友好。
三、图片压缩策略:别一股脑全压
图片压缩不是越狠越好。你想想看,一张缩略图压到10KB没问题,但一张全屏背景图压到10KB,那画质就惨不忍睹了。我的策略是分场景处理:
- 缩略图/列表图:质量压缩到60%-70%,尺寸按控件大小裁剪。用Glide的.override()指定宽高。
- 详情页大图:质量压缩到80%左右,尺寸不超过屏幕宽高。用BitmapFactory.Options的inSampleSize做采样。
- 背景图/启动图:质量压缩到90%,尺寸按需裁剪。这类图用户看得最久,画质不能太差。
// 我的常用压缩代码片段
fun compressBitmap(bitmap: Bitmap, maxSize: Int): Bitmap {
val ratio = minOf(
maxSize.toFloat() / bitmap.width,
maxSize.toFloat() / bitmap.height
)
if (ratio >= 1f) return bitmap
val matrix = Matrix().apply { setScale(ratio, ratio) }
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
}
四、三级缓存原理:一张图的生命周期
三级缓存,说白了就是内存、磁盘、网络三层。我画了个图,你看一眼就明白了:
流程其实很简单:
- 第一步:要加载一张图,先查内存缓存。内存里有没有?有就直接用,没有就下一步。
- 第二步:查磁盘缓存。磁盘里有没有?有就加载到内存,同时返回给UI。没有就下一步。
- 第三步:走网络下载。下载成功后,先写入磁盘缓存,再写入内存缓存,最后返回给UI。
这里有个细节:内存缓存用的是LruCache,说白了就是最近最少使用算法。当内存不够时,最久没用的图片会被淘汰。磁盘缓存用的是DiskLruCache,原理类似,但存的是文件。
嗯,图片优化这块,说白了就是空间换时间,时间换体验。格式选对了,库用对了,缓存策略搞清楚了,你的App在图片加载这块基本就稳了。我曾经见过一个项目,图片加载慢得离谱,一查发现磁盘缓存没开,每次都要从网络拉。开了缓存后,加载速度提升了3倍不止。
最后说一句,图片优化没有银弹。你得根据自己App的实际情况,去调参数、选方案。多测、多试、多观察,慢慢就能找到最适合你的那条路。
公众号:蓝海资料掘金营,微信deep3321