Glide源码解析之缓存
Glide缓存机制分为内存缓存和硬盘缓存。内存缓存的目的是避免重复加载图片到内存、提升加载速度;硬盘缓存的目的是避免重复从网络下载图片造成流量的浪费、同时提升加载速度
读取内存缓存
读取内存缓存的地方是在 Engine#load 方法里:
|
|
进入 loadFromMemory 方法:
|
|
内存缓存也分两级。先看 activeResources 是否存在,activeResources使用一个集合保存着图片的弱引用
|
|
如果存在的话,该图片资源EngineResource的引用次数acquired会加1。如果不存在的话则接着去内存中找:
|
|
MemoryCache 是个接口。MemoryCache有两个实现类 LruResourceCache 和 MemoryCacheAdapter(低版本适配)。重点看一下LruResourceCache类。它继承了 LruCache 类,LruCache 类里使用 LinkedHashMap 来保存最近使用的图片:
|
|
LinkedHashMap类是java自带的类,实现了LRU缓存(LRU是最近最少使用算法),可以设置一个内存的阈值,当内存超过该阈值时会移除掉最老的缓存。LinkedHashMap实现LRU算法的原理可参考 https://www.jianshu.com/p/8f4f58b4b8ab
读取硬盘缓存
硬盘缓存也分为两种情况,一种是缓存原始图片,一种是缓存转换后的图片。
读取原始图片缓存的代码是在 DataCacheGenerator 类的 startNext 方法里:
|
|
helper.getDiskCache()获取到DiskCache对象,DiskCache 是接口类,DiskCache默认的实现类是 DiskLruCacheWrapper,看一下它的 get 方法:
|
|
细看这一行代码:
|
|
|
|
lruEntries的类型也是LinkedHashMap,可知硬盘缓存也遵循LRU算法以避免磁盘缓存的文件过大。
写入内存缓存
写入弱引用缓存分为两种情况:
一种是从内存强引用LRU读取缓存后,把对应的图片从LRU移除的同时会把该图片加入到弱引用集合中。
123456789private EngineResource<?> loadFromCache(Key key) {EngineResource<?> cached = getEngineResourceFromCache(key);if (cached != null) {//把该图片加入到弱引用集合,图片的引用计数加1cached.acquire();activeResources.activate(key, cached);}return cached;}另一种情况是当首次加载图片的任务完成之后把该图片加入到弱引用集合中。如下:
12345678public synchronized void onEngineJobComplete(EngineJob<?> engineJob, Key key, EngineResource<?> resource) {//把图片加入到弱引用集合if (resource != null && resource.isMemoryCacheable()) {activeResources.activate(key, resource);}...}
写入LRU缓存
写入LRU缓存是在 Engine 类的 onResourceReleased 方法中:
|
|
对于 onResourceReleased 方法的调用时机,分为两种情况:
一种是图片的引用计数次数变为 0 的时候会调用,具体代码位置在 EngineResource 类的 release 方法中:
|
|
另一种情况是当ResourceWeakReference弱引用的对象被 gc 回收后调用 cleanupActiveReference 方法,cleanupActiveReference 同样会调用 onResourceReleased 方法:
|
|
写入硬盘缓存
一处是在DecodeJob 类中,当图片数据获取完成之后,会先解码再进行编码,onDataFetcherReady -> decodeFromRetrievedData -> notifyEncodeAndRelease 中:
|
|
encode方法具体实现如下:
|
|
另一处是在SourceGenerator类中,
|
|
进入 cacheData 方法中:
|
|
BitmapPool
bitmap 的内存管理是一个很重要的话题。glide 使用了 BitmapPool 来提升 bitmap 的复用,减少 bitmap 分配内存和回收内存的次数,从而减少 GC 的频率、应用运行更加流畅。
Android 官方提供 inBitmap 的 api 来复用 bitmap.实例如下:
|
|
BitmapPool 有两个实现类: LruBitmapPool 和 BitmapPoolAdapter 类.BitmapPoolAdapter 的实现是一个空壳子,重点看一下 LruBitmapPool 类。LruBitmapPool 提供了 put 和 get 方法来操作 bitmap。LruBitmapPool 实际上是通过 LruPoolStrategy 对象来实现存取的。LruPoolStrategy 有多个实现类,不同的系统版本会使用不同的实现类:
|
|
因为目前大多数 app 的最低系统版本在 4.4 以上了,所以 重点看一下 SizeConfigStrategy ,先看 put 方法:
|
|
GroupedLinkedMap 为 glide 自定义的数据结构,支持 LRU 算法,与 LinkedHashMap 类似。不同的是,LinkedHashMap 的 LRU 是针对的 value 值,而 GroupedLinkedMap 是针对的 key 值即图片的 size、而不是bitmap。
接下来看一下 get 方法:
|
|
findBestKey 方法实现如下:
|
|
ArrayPool
作用与 bitmapPool 类似,也是复用数组以减少内存的分配和回收,从而减少 GC 的频率、应用运行更加流畅。