0%

09-Glide-第二节课(最新Glide4.11 主线流程分析)

看源码最重要的 第一步,只管主线,简单走通,不要管支线

面试题:项目中已经大量使用 Glide ,但加载图片还是偶尔会出现内存溢出问题,说明大概原因。

答:可能在 Glide.with 的时候,传入了 Application 的作用域,或者从子线程使用了 Glide 也会导致变为 Application 作用域。在这种作用域下,不会创建空白 Fragment 对绑定页面进行生命周期管理。就会造成内存回收不及时的问题。

into 方法的时候,RequestBuilder 中会根据 ImageView 的 ScaleType 来生成不同的 ScaleType 的对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {

if (!requestOptions.isTransformationSet() && requestOptions.isTransformationAllowed() && view.getScaleType() != null) {

switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions = requestOptions.clone().optionalFitCenter();
break;
case FIT_XY:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case CENTER:
case MATRIX:
default:
// Do nothing.
}
}

return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
Executors.mainThreadExecutor());
}

总结一下,Glide 中最重要的 3 个分段 with、load、into 的三个作用:

  • with:返回 RequestManager ,里面决定是哪种作用域

    1
    2
    3
    4
    5
    6
    7
    public 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 的简化流程图结束:

简化的Glide流程图

谢谢你的鼓励