Carson带你学Android:主流开源图片加载库对比(UIL、Picasso、Glide、Fresco)
Glide两个重要点:
- 缓存机制 (内存缓存 / 活动缓存 / LRU 磁盘缓存)
1)活动缓存:在某个Activity范围,页面退出该缓存就不存在
2)内存缓存:某个App范围,应用完全退出就不存在
3)磁盘缓存:整个系统,只要不删除数据,就一直存在- 生命周期(作用域是非Application(主线程中使用) 会添加一个空白Fragment,作用域是Application(子线程中使用) 不会添加一个空白的Fragment(跟随App的关闭而销毁))
空白Fragment:
android.app(v4) --> RequestManagerFragment
androidx --> SupportRequestManagerFragment
创建Fragment 的原因
因为glide无法直接获取activity的生命周期,通过activity的FragmentManager中加入一个隐藏的fragment,因为fragment与传入的activity生命周期一致,所以只要监听这个RequestManagerFragment就能实现生命周期管理
图片加载流程:
① 先从活动缓存(非LRU算法,正在显示的图片)进行获取,找到里面显示;
② 如果活动缓存中找不到,再从LRU内存缓存中进行获取,如果找到,将LRU内存缓存移动到活动缓存,进行显示;
活动缓存和LRU内存缓存都是运行时缓存,如果把app杀掉,这两个缓存就不会存在,但是磁盘缓存会存在。
活动缓存与LRU内存缓存是互斥的,是移动操作(即两个缓存里面只能有一个缓存里面有);而活动缓存与磁盘缓存 资源不是互斥的,不是移动剪切操作是复制操作(两个缓存里面都可以有)。
③ 如果内存缓存找不到,就去LRU磁盘缓存(持久缓存)进行查找,如果找到,就从磁盘缓存中复制一份到活动缓存进行显示;
④ 如果LRU磁盘缓存找不到,就通过访问外置HTTP进行网络加载或者读取IO流,如果从外置HTTP加载或IO流能够加载成功,就把加载的数据保存到LRU磁盘缓存。
1. 时序图
2. 时序图中一些关键步骤中的解析说明:
Glide.with 将当前显示的页面对象传入Glide
SupportRequestManagerFragment 空的fragment 监听当前页面的生命周期
ActivityFragmentLifecycle 观察者模式,fragment的生命周期交给了他
Request previous = target.getRequest();
if(previous != null) {
判断当前的ImageView是否存在了请求
如果存在 将请求取消
并且回收资源
previous.clear();
this.requestTracker.removeRequest(previous);
previous.recycle();
}
New BitmapRequest 创建请求 request是一个接口
Request request = this.buildRequest(target);
开始执行请求
this.requestTracker.runRequest(request);
请求的实现类
GenericRequest
Begin开始请求
DataFetcher 加载器 是一个接口
返回的子类型 是根据第一个参数 model 来确定
DataFetcher dataFetcher = modelLoader.getResourceFetcher(this.model, width, height);
this.loadStatus = this.engine.load(this.signature, width, height, dataFetcher, this.loadProvider, this.transformation, transcoder, this.priority, this.isMemoryCacheable, this.diskCacheStrategy, this);
把请求丢给图像处理引擎 处理图片
判断内存是否缓存了这张图片
EngineResource cached = this.loadFromCache(key, isMemoryCacheable);
负责加载每一个请求,它里面有线程池成员变量
EngineJob
实现Runable接口,具体执行请求逻辑 run()
功能:请求资源,处理资源,缓存资源
EngineRunnable
解析图片,返回glide里面的resource对象
resource = this.decode();
专门解析图片
decodeJob
通过加载器 实现图片加载 它是运行子线程 run方法被调用
加载网络图片时 data是inputStream类型
Object data = this.fetcher.loadData(this.priority);
解析Urlconnection返回的InputStream流对象
StreamBitmapDecoder
3. 流程图
=========================
问题1:
Glide不能在子线程中with?
答:子线程不会去添加生命周期机制,主线程才会来个 空白的Fragment 监听Activity/Fragment的变化。
with()–>最终返回RequestManager对象
load()–>最终返回RequestBuilder对象
into()–>最终调用ImageViewTarget子类DrawableImageViewTarget的setResource()方法加载图片
@Override
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
问题2:为什么设置了内存缓存还要设置活动缓存呢?
答:内存缓存也叫LRU缓存 / 二级缓存
活动缓存也就是活动资源 / 活跃缓存 / 前台缓存
假如只有内存缓存,并且该设置缓存的maxSize=3,界面显示的imageView对应的资源正好是第一个位置的资源。现在要保存第4个资源到内存缓存中,根据LRU算法就会把最近最久未使用的资源移除掉,那么就会导致图片加载失败,从而导致应用崩溃。为了解决这样的问题,引入一个活动缓存,该缓存是非LRU缓存,用弱引用来存储数据。
imageView加载数据流程:
1)假如活动缓存中没有数据,就去访问内存缓存;
2)如果内存缓存中有数据,将内存缓存的数据移动到活动缓存,用于显示画面,内存缓存中的该资源会进行删除(这个地方注意,对应一个资源只能在内存缓存和活动缓存中一个,它们是互斥的)
3)假如用户将activity关闭,此时将活动缓存中的数据移动到内存缓存中
4)假如用户打开该activity界面,加载imageView,查找该活动缓存没有该资源,就会去内存缓存中查找,查找到该资源将该资源移动到活动缓存中,将内存缓存中的该资源删除
问题3:项目中大量的使用了Glide,偶尔会出现内存溢出问题,请说说大概是什么原因?
答:尽量在with的时候,传入有生命周期的作用域(非Application作用域),尽量避免使用了Application作用域,因为Application作用域不会对页面绑定生命周期机制,就回收不及时释放操作等…
问题4:使用Glide为什么要加入网络权限?
答:等待队列/运行队列 执行Request —> 活动缓存 —>内存缓存 —> jobs.get检测执行的任务有没有执行完成 —> HttpUrlFetcher.HttpURLConnection
问题5:使用Glide时,with函数传入Application后,Glide内部会怎么处理?
答:在MainActivity中,MainActivity销毁了,并会让Glide生命周期机制处理回收,只有在整个APP应用都没有的时候,跟随着销毁
总结:
-
安装app的加载流程
第一次的时候,去网络下载图片,保存到磁盘缓存中
第二次的时候,直接在活动缓存中,找到了资源
第三次的时候,直接在活动缓存中,找到了资源
… …
第N次的时候,直接在活动缓存中,找到了资源 -
按home键,app退到后台
把Activity给返回回去的时候,进行释放,活动缓存释放(活动缓存移动到内存缓存)
又一次加载的时候,从内存缓存中获取了
下一次加载的时候,就是从活动缓存获取了
第二次的时候,直接在活动缓存中,找到了资源
第三次的时候,直接在活动缓存中,找到了资源
… …
第N次的时候,直接在活动缓存中,找到了资源 -
杀掉app
把App给杀掉,整个活动缓存、整个内存缓存都没有了(app消掉运行时都会被销毁),只有磁盘缓存
首次冷启动,所以从磁盘缓存中获取
第二次的时候,直接在活动缓存中,找到了资源
第三次的时候,直接在活动缓存中,找到了资源
… …
第N次的时候,直接在活动缓存中,找到了资源
补充:
一、 普通的三级缓存:
1、内存缓存,优先加载,速度最快
2、本地缓存,次优先加载,速度快
3、网络缓存,最后加载,速度慢,浪费流量
三级缓存策略,最实在的意义就是减少不必要的流量消耗,增加加载速度。
普通的三级缓存对所有文件有适用,Glide三级缓存针对的是图片类型文件,其实对音频/视频文件也有参考价值,其他文件的缓存作用就不大了。
二、 Glide三级缓存分类:活动缓存、内存缓存、磁盘缓存,三级缓存范围:
1、活动缓存:在某个Activity范围,页面退出该缓存就不存在
2、内存缓存:某个App范围,应用完全退出就不存在
3、磁盘缓存:整个系统,只要不删除数据,就一直存在
图片缓存不会同时存在活动缓存和内存缓存。
三、三级缓存的作用:
1、活动缓存:分担内存缓存的负担,
2、内存缓存:加快数据读取
3、磁盘缓存:进行永久性保持
四、活动缓存的说明:
1、活动缓存并不是我们熟悉的内存缓存,是Glide自己定义的一种缓存策略。
2、本质上就是HasMap,用了一次就缓存,以后需要就直接拿,不需要就清除这个缓存。
3、该策略的存在也是为了及时释放内存,不需要等整个应用退出再释放内存,减轻应用内存负担。
4、活动缓存比内存缓存小,如果活动缓存满了,会自动写到内存缓存。
5、系统会对内存缓存进行自动管理,只要不是快速存放大内存文件,并且不一直占有内存对象,都不会内存溢出。
五、内存缓存的说明:
内存缓存是系统自身会管理的,但是可以继承LruCache,做进一步管理
六、磁盘缓存的说明:
1、磁盘缓存本质是本地文件缓存,但是通过普通的文件写入读取效率不高。
2、Glide中使用了DiskLruCache框架进行数据保存和读取。
3、效率高的主要原因是:磁盘缓存对图片文件进行了加密和压缩处理。
七、Glide三级缓存的使用
1、优先从活动缓存获取
2、活动缓存没有就再内存缓存中寻找
3、内存缓存没有,就去磁盘缓存读取
4、磁盘缓存没有就去网络获取本地文件读取