Glide 图片预加载的正确姿势

预加载下一个页面的图片

Glide 的preload 应该有不少人都用过,但应该多数情况下都是在一个列表页中preload。 有一种情况的preaload 会复杂一些

列表页A ---> 详情页B。 例如小红书 你会发现列表页到详情页的时候 多数情况下,详情页的图片加载是瞬间就好了的。

有人会说,这个不是很简单吗,我在列表页中预下载一下详情页的图片。 但其实你如果自己做的时候试试看, 仅仅用预下载这个机制 是完全做不到小红书的效果的。

拿Glide 这个框架来举例,详情页B 第一次加载图片的时候 就算你的图片已经下载好了,但是依旧能看到有一个loading的过程,稍微录个屏 就能明显看到activity 在切换过程中的 图片加载过程

但是你如果退出页面再进入一次详情页,你就会发现这个图片加载的过程没了

其实这两次的区别很好理解,第二次进入详情页的时候,Glide 是直接从内存中取的目标Bitmap。 所以很快

截取两张图片看一下

我们第一次进入详情页的时候

能够明显看出来 文字渲染出来的时候,图片还没渲染出来 注意图片其实根本没有下载的过程,因为图片本身已经在列表页下载好了, 但是没有在内存中而已。

当我们退出详情页,第二次再进入该页面时

明显能看到 文字渲染出来的时候 图片也是直接展示的,几乎没有任何loading的时间

何时才能命中

前面分析过 加载差异的原因,那么对于glide来说,我们到底要通过哪个地方的函数调用才能知道 我的内存缓存到底命中了没有?

Engine类的load方法中 有一个 loadFromMemory的方法调用

简单来说,这个loadFromMemory 如果返回null 则意味着 你的内存缓存没有命中,

这个方法内部实现我就不说了,有兴趣的自己去看下就好,其实也没啥特殊的,无非就是 Glide的内存缓存做了两层的分级

preload的正确用法 到底是什么?

我们大概搞清楚了 预加载图片的逻辑以后,可以自己尝试写一下了,经过一番搜索后得出的代码如下:

写法1

scss 复制代码
           Glide.with(this)
               .load(url)
               .preload()

这个方法 肯定是不行的,看上去preload了,但是内存缓存并没有命中

再搜索一下

写法2

kotlin 复制代码
            Glide.with(this)
                .load(url)
               .preload(dpToPx(this,200f), dpToPx(this,200f))

这个看上去有点靠谱了,大家都知道glide的内存缓存 其实缓存的是 decode之后的图片,也就是说 你原图是200200,但是你展示区域是100100 那么最终缓存的是100*100的图片

我们这个写法 preload写死了 宽高,那总能命中了吧。。。

试了一下,我擦 还是不行

再接着换各种关键词搜索,答案五花八门,还有的说什么要设置DiskStrag之类的,越来越离谱,

话说你们这些写blog的人自己都不试一下 自己的代码真的能生效吗?

为啥内存缓存不命中?

其实最关键的就是

这个key 其实在后续的流程中 主要是作为 map的key使用, 刷过面试八股文的同学 还记得map的 get方法实现吗?

我们看下这个key的equals的方法实现

这个model 你就理解成图片url 就好,其他的宽高也都好理解 注意了这个宽高得是imagview的宽高

除此之外 最关键的就是

看到这2个 应该能反应出来 大概是啥东西了吧

glide的apply方法 传递的就是这些关键影响因子

那是不是我们设置了apply 就一定没问题?

scss 复制代码
RequestOptions()
    .placeholder(placeHolderPic)
    .error(placeHolderPic)

比如说随便设置一下上面的options, 这样的key能一样吗?

其实还是不行 这里主要是因为preload 没有涉及到具体的imageview,但是你真正加载图片的时候 一定是会有into(imageview)的过程的,这个过程中 glide会给你默认加一下 transformations (如果你没有设置过transformation的话)

这就会导致 你preload的 key和你实际加载的key 还是无法一致 最终导致缓存不一致

改成下面的写法 就可以完美命中了

scss 复制代码
val options2 = RequestOptions()
    .transform(CenterCrop(), RoundedCorners(20))

总结下预加载并且内存命中的几个必要因素

  • preload必须指定宽高,宽高要和最终imageview的宽高保持一致,单位是px
  • 必须applay options
  • RequestOptions 必须指定 transform

如何衡量收益?

虽然肉眼看上去,体验有一些收益,但是老板如果问你技术指标的具体收益呢,总不能拍脑门吧 这里其实可以考虑用 内存缓存命中率这个指标 但好像 glide 并未对外暴露这个接口?

看Engine这个类的实现, 这个类虽然是public的,而且可以通过setEngie这个方法来替换glide 的engine具体实现

但其实关键的东西 都无法重写,比如这个loadFromMemeory这个方法 他是private的 我extends Engine以后 并没有办法调用或者重写这个方法

有人说 哪你可以重写load方法,这个方法是公开的啊

但是这个EngineKey 不是public的 我还得自己custom一个key 然后替换掉原来的key

感觉有点套娃了,这样写负担太大,如果一定要这样做的话,我建议还是直接基于glide 的源码自己直接修改好了,

这样灵活定制效果会好很多。

相关推荐
大白要努力!1 小时前
android 使用SQLiteOpenHelper 如何优化数据库的性能
android·数据库·oracle
Estar.Lee1 小时前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
Winston Wood1 小时前
Perfetto学习大全
android·性能优化·perfetto
Dnelic-4 小时前
【单元测试】【Android】JUnit 4 和 JUnit 5 的差异记录
android·junit·单元测试·android studio·自学笔记
Eastsea.Chen6 小时前
MTK Android12 user版本MtkLogger
android·framework
长亭外的少年14 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
建群新人小猿16 小时前
会员等级经验问题
android·开发语言·前端·javascript·php
1024小神17 小时前
tauri2.0版本开发苹果ios和安卓android应用,环境搭建和最后编译为apk
android·ios·tauri
兰琛17 小时前
20241121 android中树结构列表(使用recyclerView实现)
android·gitee
Y多了个想法18 小时前
RK3568 android11 适配敦泰触摸屏 FocalTech-ft5526
android·rk3568·触摸屏·tp·敦泰·focaltech·ft5526