LEEDOM

Feb 18, 2020

Glide的缓存策略源码分析

接着我们先看看第一步中的load方法:

从下面的代码可以看到,先会从缓存中获取资源,如果不存在,才会新开线程从硬盘获取或者从其他远端(文件、网络等等)加载新资源。

glide_engine_load

内存缓存

内存缓存的设置也有两级,这里就是引言中使用的弱引用+Lru的方式:

glide_engine_load_detail

activeResource中的逻辑很简单清晰,内部维护了一个HashMap用键值对的形式保存了keyWeakReference,这里直接去获取,当存在就返回:

glide_engine_load_weak

如果弱引用中不存在了,就从Lru中去获取,从方法的调用上看也是一层层的获取,当缓存存在时,从Lru中取出来,并重新加入弱引用中。

glide_engine_load_lru

到这里为止,都是正常的流程。但我们知道,弱引用会在下一次GC时被回收,如果被回收了又没有加入Lru不就炸了么?对弱引用的使用不太了解的同学也许会存在这样的疑问,Reference有一个构造方法可以传入一个ReferenceQueue,这个队列的作用就是用于检测对象本身可达性发生变化时,通过Map来反向获取指向对象的包装引用(即Reference对象),将其放入到队列,来进行后续的操作,这里有一篇关于引用队列的介绍

正如上面的介绍一样,Glide的内部也是通过引用队列来实现弱引用对象被回收时,将其放入Lru中。

流程如下:

glide_cache_weak

  1. 当我们第一次加载成功后,会创建弱引用对象来缓存它。
  2. 创建弱引用对象时也会传入弱引用队列。
  3. 同时这个对象资源在初始时会新开一个线程去无限循环的检测弱引用队列。
  4. 弱引用队列的remove()方法是个阻塞方法,当队列中存在元素时才会执行,所以当gc()回收弱引用对象后,队列中就有值了
  5. 这个时候无限循环的检测中就会拿到刚刚回收的弱引用对象。就会将该对象放到lru中去缓存。
  6. 另一边是如果使用的资源被主动释放了,Lru内部是维护了一个引用计数acquired的数字,当这个数子为0时就代表没有引用了,也会缓存到Lru中去。

看看具体的实现:

首先ActiveResources的构造方法中,会生成一个子线程执行检测任务,随时检测引用队列的remove()方法(这是一个阻塞方法,队列没有值的时候会一直阻塞),当还没有引用别回收时,这个检测的线程就会因为阻塞而休眠,而一旦出现了被回收的对象,就会被引用队列唤醒,执行remove方法的后续。

glide_ar_init

在资源首次被添加时,会去生成一个弱引用的对象,然后在上面的检测中检测它的回收

glide_ar_active

cleanupActiveReference中,引用的资源还在时,就会新建一个resource然后通知engine把这个新的存到lru中,这样就完成了一次弱引用到lru的转换了

glide_ar_turn2_lru

这里还有一个细节就是每一次获取成功后都会调用cached.acquire()这个方法,这个方法内会自增一个计算的变量,用于统计资源的使用,每次release时都会自减,当这个值为0时说明没有使用他的地方了,同样的也会调用listener.onResourceReleased方法来将该变量存到Lru中。

硬盘缓存

硬盘缓存的使用是DiskLruCacheGlide也提供了几种缓存的策略如下

  • DiskCacheStrategy.NONE: 表示不缓存任何内容。

  • DiskCacheStrategy.SOURCE: 表示只缓存原始图片。

  • DiskCacheStrategy.RESULT: 表示只缓存转换过后的图片(默认选项)。

  • DiskCacheStrategy.ALL : 表示既缓存原始图片,也缓存转换过后的图片。

在最开始的图中,waitForExistingOrStartNewJob这个命名就基本说明了接下来的流程,先从本地的硬盘缓存中查找,如果没有就从远端资源获取。只不过这个地方的源码稍微有点绕。

enginewaitForExistingOrStartNewJob方法中构建了DecodeJob对象,并且开启了新的线程去执行它(这个对象实现了runnable)。

glide_decode_job_run

根据上面不同中的runReason会生成不同的stage,然后生成不同的generator,主要有三种:

  • ResourceCacheGenerator:这个类中会去寻找经过处理之后的资源
  • DataCacheGenerator:这个类中会去寻找原始的资源
  • SourceGenerator:这个类就是去远端获取资源

glide可以同时缓存原始的资源和经过转换后的资源,当然这都是根据你设置的缓存策略来实现了。在DecodeJob中会去调用他们的startNext()方法,接下来就分别看看上面两个缓存的实现:

glide_genertor_start

这两个里面的实现大体逻辑都差不多,只是对于的key不一样,而这个helper.getDiskCache最后拿到的就是DiskLruCacheWrapper的实例,在他的getDiskCache中去获取磁盘的缓存:

glide_disk_lru_cache

到这里,会通过之前设置好的callback一步步调用到engine中,返回这个磁盘缓存的资源。

OLDER > < NEWER