Glide 作为常用的图片加载框架,框架层面已经对内存方面有不少优化,但作为一个图片框架,确保正确性一定是第一位的,因此在应用层还可以在适当的场景做一些额外的优化,当然你需要了解优化设置可能产生的问题。另外框架设计复杂性是很高的,但暴露给上层的 API 又非常简单,这导致我们忽略一些基本的工作原理,从而有意无意错误的使用框架。
这不是一篇 Glide 源码分析类的文章,如果对 Glide 使用还不够熟悉,不建议直接阅读。 以下文档结论基于 Glide v4+ 版本。
手动调用 override 的方式,对于解码图片的大小优先级最高,会默认跳过采样策略计算,如果你明确知道显示区域需要多大的图片可以使用此方法,避免图片放大(第5条)。如果想使用原图大小,可以使用 override(Target.SIZE_ORIGINAL)。
preload 相关API 是不需要指定 Target 的,Glide 内部会默认加载原图,如果想明确 UI 上需要的尺寸,需调用重载的方法 preload(int width, int height)
定制图片尺寸。
Glide 解码图片时是不会考虑 ImageView 的 scale_type 属性的,来看这个例子:
由于图片设置了 centerCrop 属性,在直观看图片会按等比居中缩放,但由于 decode 发生在 scaleType 生效之前,因此使用 Glide 加载图片时会以 ImageView 的实际尺寸(即全屏) 的宽高来解码图片,由此产生了不必要的内存开销。
解决方案比较多,常规的可以提前通过原始图和容器宽高比,计算出实际需要显示的图片尺寸,并使用 override(width, height) 方法强制指定解码图片的大小。亦或前提知道图片的比例,可使用 layout_constraintDimensionRatio 约束 ImageView 控件的大小。
原因是 Glide rounderCorner 发生在图片decode 之后,经一轮变换会产生一个新的位图(官方描述为所有基于BitmapTransformation 变换产生的位图会被存储在 BitmapPool 中,并最终按 LRU 算法释放),而 CardView 是通过canvas.drawRoundRect(mBoundsF, mRadius, mRadius, paint)
,drawRoundRect 会转化成一系列绘图指令到 GPU,不会在堆内存产生额外的内存开销。
以下是实验数据,在三种情况下做内存数据对比,前置条件是加载一张 750x750 大小的不带透明度的 jpeg 格式的网络图片。按常规来说,如果使用 ARGB_8888 格式解码,产生的内存开销为 750x750x4 = 2197 kb,如果使用 RGB_565 则能减少一半的内存开销。
场景 | Native Heap(KB) | Graphics(KB) |
---|---|---|
RGB_565 & 默认直角 | 12730 | 5410 |
RGB_565/ARGB_8888 & 圆角 Transform | 17326 | 6564 |
RGB_565 & CardView 圆角 | 13633 | 5417 |
可以看到是 NativeHeap 有明显的降低,推测与RounderCorner 额外变换导致的位图临时开销有关,Graphics 降低 1M 左右与预期相符。
在背景知识中提到如果原图比控件容器大,则会缩小图片,并不会导致内存浪费。但如果原图比容器小会发生什么呢?例如:原图为 100x100,但容器为 200x200,那么默认情况下 decode 出一个 200x200 的位图。事实上,Glide 整体的图片缩放机制是通过内部的 DownsampleStrategy 完成的,其内部定义了与 ScaleType 类似的采样策略,默认的采样策略为 CENTER_OUTSIDE: 当原图尺寸 < 控件尺寸时会成比例放大至控件大小。另外还有其他采样策略,使用 AT_LEAST、AT_MOST、CENTER_INSIDE 模式,当明确知道原图尺寸 < 控件尺寸时均不会放大原图;反之,当原图尺寸 > 控件尺寸是可能会出现解码出大于控件尺寸图的情况。
使用示例:
Glide.with(this)
.asBitmap()
.load(url)
.apply(RequestOptions.downsampleOf(DownsampleStrategy.AT_LEAST))
.into(imageView)
有效降低内存,对于多图文或 Feed 列表的 App,开启该配置可降低 10-20M 内存。示例全局开启 RGB_565 解码配置。
@GlideModule
class GlideConfig : AppGlideModule() {
override fun applyOptions(context: Context, builder: GlideBuilder) {
super.applyOptions(context, builder)
builder.setDefaultRequestOptions(RequestOptions().format(DecodeFormat.PREFER_RGB_565))
}
...
}
但与此同时你需要了解在一些情况下 RGB_565 可能不生效,甚至产生奇怪的显示效果。
1/2 比较好理解,对于3,常见的就是高斯模糊效果(毛玻璃),该效果目前是通过 RenderScript 相关API,此 API 要求应用视图必须开启硬件加速。在此情况下,同时开启 RGB_565 配置会导致出现异常,可能会返回一个空的位图,在部分设备上可能会返回一个异常的模糊效果。
根据以上 Glide 对图片的缩放处理可知,默认情况下一张图片占用的最大内存只与控件宽/高和解码格式有关。
在整个图片处理的过程中,图片降质只是减少图片的下载速度,对内存占用并无直接影响。
为了帮助到大家更好的掌握好 开源框架相关知识点,这准备了 Android 开源框架的学习手册↓↓↓
有需要的可以复制下方链接,传送直达!!!
https://qr21.cn/CaZQLo?BIZ=ECOMMERCE