看源码最重要的 第一步,只管主线,简单走通,不要管支线
面试题:项目中已经大量使用 Glide ,但加载图片还是偶尔会出现内存溢出问题,说明大概原因。
答:可能在 Glide.with 的时候,传入了 Application 的作用域,或者从子线程使用了 Glide 也会导致变为 Application 作用域。在这种作用域下,不会创建空白 Fragment 对绑定页面进行生命周期管理。就会造成内存回收不及时的问题。
into 方法的时候,RequestBuilder 中会根据 ImageView 的 ScaleType 来生成不同的 ScaleType 的对象:
1 | public ViewTarget<ImageView, TranscodeType> into( ImageView view){ |
总结一下,Glide 中最重要的 3 个分段 with、load、into 的三个作用:
with:返回 RequestManager ,里面决定是哪种作用域
1
2
3
4
5
6
7public RequestManager get(FragemntActivity activity) {
if(Util.isOnBackgroundThread()) {
//子线程,Application
} else {
//Activity/Fragment 作用域
}
}load:最终返回 RequestBuilder
into : 最终返回 ImageViewTarget
根据宽高、签名、等等一系列信息,作为某个图片缓存的 key ,在内存缓存和磁盘中获取这些缓存的图片。
关于缓存
Glide 里面有活动缓存(ActiveResources)和内存缓存(Cache,也叫二级缓存,也叫LRU缓存),他们都在内存里面。那为什么要在内存里面设置 2 级缓存?Glide 将正在显示的图片都放在活动缓存里面(活动缓存里面都是使用 WeakReference 来引用图片),然后其他的图片都放在内存缓存里面。这又是什么讲究?内存缓存是 LRUCache 实现的,它可以存储很多图片,假如没有活动缓存这一级,而是直接使用内存缓存的话,那么在缓存的图片数量或者大小超限的时候,正在使用的图片就可能被清除掉,导致崩溃 Bug(比如RecyclerView里面使用,划过来可以,再划回去重新加载的时候被回收了就崩溃了) ,这也是为什么需要设计 2 级缓存。
当应用需要获取图片的时候,首先从活动缓存中获取,如果没有,则去内存缓存中获取,如果命中了,则将 内存缓存中的图片添加到活动缓存中,并且将图片资源从内存缓存中删除。当引用的页面(Activity/Fragment)关掉之后,活动缓存的图片又可以放入内存缓存中去,如果页面再次打开,就又可能从内存缓存中加载进入活动缓存。活动缓存和内存缓存中只能存在一份缓存,不可能同时在 内存缓存和活动缓存中都存在。活动缓存里面会做引用计数,如果计数为 0 的时候,会将图片放回到内存缓存里面。
面试官:Glide 源码中到处都是接口,我们应该怎么阅读?
答: 我们要找里面的伏笔。比如,我们 getRequest 的时候,要一直追踪下去,看看到底这个 getRequest 返回的是 Request 的哪个子类。不然很难知道具体是哪个类实现。
面试官:使用 Glide 为什么需要加入网络权限?
答: Glide 中执行图片请求的时候 有等待队列和运行队列2个队列,并且有 活动缓存和内存缓存2级缓存,如果这2级缓存都没有命中的话,需要通过网络去获取资源。并且,还可以通过 job.get 去判断目前任务是否完成,最终使用 UrlConnection 去完成最终的网络请求。
我们平时使用 Glide 一般都是传入 String 类型的 url ,然后会返回 InputStream 这种流,decode 的作用就是将流转成 Bitmap 。
面试官:使用 Glide 的时候,如果 with 函数在子线程调用,会有什么问题?
答:子线程不会去添加生命周期机制,主线程才会添加空白Fragment 监听 Activity/Fragment 的生命周期变化。
面试官:with 函数传入 Application ,会怎么样?
答:如果传入的是Activity 或者 Fragment ,当它们销毁的时候,Glide 会回收当前页面加载的图片任务和资源,但是如果传入的是 Application ,那么只有当应用结束的时候资源才会跟随销毁了。
如果ImageView 很小,但是图片是个很大的图片,Glide 会给做优化,只会给目标大小的图片就可以了。
最后,以一张Glide 的简化流程图结束: