Android 图片加载与内存控制
Android 图片加载与内存控制
图片是 Android 应用中常见的内存大户。文件体积很小的压缩图,解码后也可能占用数兆内存;列表快速滚动还会同时产生多个请求、变换和视图复用。图片优化的重点是按展示需求解码、复用统一缓存,并让请求跟随视图生命周期取消。
一、理解解码后的内存
位图内存主要由像素宽高和像素格式决定,而不是由文件体积决定。一个高分辨率图片即使压缩得很好,完整解码后仍可能非常大。
因此,列表中的小缩略图不应直接解码原始大图。加载组件需要知道目标尺寸,按接近展示区域的大小采样。
二、复用项目图片加载组件
成熟项目通常已经统一处理内存缓存、磁盘缓存、请求合并、采样和生命周期。业务页面只描述资源标识、目标视图和必要变换。
fun bindCover(item: ArticleItem) {
imageLoader
.load(item.coverKey)
.resize(targetWidthPx, targetHeightPx)
.placeholder(R.drawable.cover_placeholder)
.into(binding.coverImage)
}
示例方法名以项目组件为准。不要在单个适配器内再维护一套静态位图缓存,否则难以统一容量与回收策略。
三、确保列表复用不会串图
RecyclerView 单元格被复用后,旧请求可能晚于新请求完成。图片组件应在目标视图绑定新资源时取消或覆盖旧请求。
override fun onViewRecycled(holder: ArticleViewHolder) {
holder.clear()
super.onViewRecycled(holder)
}
fun clear() {
imageLoader.clear(binding.coverImage)
binding.coverImage.setImageResource(R.drawable.cover_placeholder)
}
如果统一组件已经自动关联视图请求,额外清理应遵循其使用约定,避免重复操作造成闪烁。
四、确定目标尺寸
控件尚未完成布局时,直接读取宽高可能为零。可以使用固定设计尺寸对应的像素值、布局完成回调,或让图片组件根据目标控件自动计算。
不要使用屏幕宽度作为所有图片的解码尺寸。横向列表、双列卡片和头像的实际需求不同。
五、合理选择裁剪方式
centerCrop 会填满区域并裁掉部分边缘,fitCenter 会完整显示但可能留白。选择由内容语义决定:人物证件、二维码等不能随意裁剪,装饰封面则通常允许。
圆角、模糊和颜色变换会增加计算与缓存键数量。相同视觉规则应使用统一参数,避免每个页面产生无法复用的缓存结果。
六、控制缓存策略
内存缓存用于快速复用当前会话图片,磁盘缓存减少重复读取和传输。缓存并非越大越好:
- 内存过大会挤压页面和业务对象空间。
- 磁盘过大会增加清理和存储压力。
- 带身份或敏感内容的图片不一定允许长期缓存。
- 实时图片需要明确失效规则。
缓存键应包含真正影响图像结果的资源版本和变换参数,不能使用不稳定对象地址。
七、避免在主线程解码和变换
图片读取、解码和复杂变换应在后台执行,最终设置到视图回到主线程。业务层不要在绑定方法中同步读取文件或创建大位图。
预加载可以改善即将出现内容的体验,但要控制数量并支持取消。用户快速滚动时,远离可视区域的预取任务应停止。
八、管理自有 Bitmap
画布、截图和编辑功能可能需要自行创建位图。此时应:
- 根据实际输出尺寸创建。
- 不在每一帧反复分配。
- 生命周期结束后解除引用。
- 大批处理时逐张释放中间结果。
- 在创建前评估内存峰值,而不只看最终文件。
现代系统通常由垃圾回收管理位图内存,手工回收行为要符合项目最低版本和现有封装,不能照搬旧方案。
九、处理生命周期
页面停止或销毁后,不再需要的请求应取消。图片组件如果感知 Activity、Fragment 或视图生命周期,可以自动暂停与恢复。
不要让单例长期强持有页面 Context 或图片视图。需要应用级上下文时,使用项目统一提供的应用上下文。
十、定位内存问题
验证图片场景时可以:
- 连续滚动包含大量图片的长列表。
- 重复进入退出图片详情页。
- 记录解码尺寸、缓存命中和内存峰值。
- 检查页面销毁后图片视图是否仍被持有。
- 在低内存回调后确认缓存能按策略收缩。
瞬时内存上升不一定是泄漏,重点观察重复操作后基线是否持续增长。
总结
Android 图片内存优化的核心是按目标尺寸解码,并把缓存和生命周期交给统一组件管理。列表复用时取消旧请求,谨慎使用变换与预取,自行创建位图时控制峰值。通过实际滚动和页面进出测量,才能区分缓存占用、临时峰值与真正泄漏。
- 点赞
- 收藏
- 关注作者
评论(0)