Glide
类中与内存缓存有关的变量Glide#memoryCache
,如果用户没有通过 GlideBuilder#setMemoryCache(MemoryCache)
设置,则默认为 LruResourceCache
(继承自 LruCache
)。
在 Engine#load()
方法中,就先根据需要加载的资源的相关参数,来生成唯一的 key
值,而该值与从缓存机制有关。
// Engine.java
public synchronized <R> LoadStatus load(...) {
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
// 首先根据目标资源的相关参数得到对应的 key 值
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
//(1)先从 ActiveResources 里面去获取
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
// (2)如果没有,则从内存缓存中去获取
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
// 在两个方法都没有获取到缓存的情况下,才会继续向下执行,从而开启线程来加载图片
...
}
(1) loadFromActiveResources()
private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
// 先判断是否禁用内存缓存
if (!isMemoryCacheable) {
return null;
}
// 从 activeResources 中去获取资源
EngineResource<?> active = activeResources.get(key);
if (active != null) {
// 标记资源被引用数量 +1
active.acquire();
}
return active;
}
activeResources
为 ActiveResources
对象,其内部有一个 HashMap
,即持有着 key
值对应的资源对象的弱引用。
activeResources 的目的是为了 缓存正在使用中的图片,从而可以保护这些图片不会被 LruCache 算法回收掉。
(2) loadFromCache()
private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
// 从 LruCache 对应的内存缓存中去获取
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
// 将取出的资源添加到 activeResources 中
activeResources.activate(key, cached);
}
return cached;
}
private EngineResource<?> getEngineResourceFromCache(Key key) {
Resource<?> cached = cache.remove(key);
final EngineResource<?> result;
if (cached == null) {
result = null;
} else if (cached instanceof EngineResource) {
// Save an object allocation if we've cached an EngineResource (the typical case).
result = (EngineResource<?>) cached;
} else {
result = new EngineResource<>(cached, true /*isMemoryCacheable*/, true /*isRecyclable*/);
}
return result;
}
loadFromCache()
则是从前面说的默认为 LruResourceCache
中去获取目标资源。如果目标资源存在,则如 LruResourceCache
中取出,并加入到前面说的 activeResources
中。
从内存缓存中读取资源的逻辑大概就是这些。概括一下来说,就是如果能从内存缓存当中读取到要加载的图片,那么就直接进行回调,如果读取不到的话,才会开启线程执行后面的图片加载逻辑。
补充,根据郭神的说的,对于内存缓存,使用到 ActiveResources
和 LruResourceCache
,目的是为了保护正在被使用的图片不会被 LruCache
算法回收掉。
Engine
类实现了 EngineJobListener
接口,当资源加载完成后,就回调 Engine#onEngineJobComplete()
方法。
// Engine.java
public synchronized void onEngineJobComplete(
EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
// A null resource indicates that the load failed, usually due to an exception.
if (resource != null) {
// 为 EngineResource 资源对象设置 ResourceListener 监听,即 Engine 对象自身,
// 因为Engine 实现了 ResourceListener 接口。
resource.setResourceListener(key, this);
// 如果资源允许被缓存,则将其添加到 activeResources 中
if (resource.isCacheable()) {
activeResources.activate(key, resource);
}
}
jobs.removeIfCurrent(key, engineJob);
}
这里注意的是,加载好的资源并不是直接被缓存到 LruResourceCache
中的,而是先被缓存到 activeResources
中。
而缓存到 LruResourceCache
中,则与第 7 行设置 ResourceListener
有关。之前有说过 EngineResource#acquire()
方法,该方法中就使用 EngineResource
对象的成员变量 acquired
来记录图片资源被引用的次数,调用 acquire()
方法会让变量加 1,而调用 release()
方法则会使变量减 1。
也就是说,当 acquired
变量大于 0 的时候,说明图片正在使用中,也就应该放到 activeResources
弱引用缓存当中。而经过 release()
之后,如果 acquired
变量等于 0 了,说明图片已经 不是正在被使用状态 了。
void release() {
synchronized (listener) {
synchronized (this) {
if (acquired <= 0) {
throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
}
// 当 acquired 为 0 的时候就会回调设置的 ResourceListener 接口的 onResourceReleased() 方法
if (--acquired == 0) {
listener.onResourceReleased(key, this);
}
}
}
}
前面说 Engine
也实现了 ResourceListener
接口,
// Engine.java
public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
activeResources.deactivate(cacheKey);
if (resource.isCacheable()) {
cache.put(cacheKey, resource);
} else {
resourceRecycler.recycle(resource);
}
}
在 onResourceReleased()
方法中,就会将已经不是正在被使用的图片资源从 activeResources
中移除,然后根据缓存状态,将其转移至 LruResourceCache
中,或者回收释放。
Glide 默认在磁盘中缓存的图片并不是原始的图片,而是经过一系列操作(如压缩、转换为目标高宽)转换来的(Resource)。当然也可以设置缓存原始图片(Source)。(因而两种图片对应的 key
值也是不同的)
DiskCacheStrategy.NONE: 表示不缓存任何内容。
DiskCacheStrategy.SOURCE: 表示只缓存原始图片。
DiskCacheStrategy.RESULT: 表示只缓存转换过后的图片(默认选项)。
DiskCacheStrategy.ALL : 表示既缓存原始图片,也缓存转换过后的图片。
磁盘缓存实现类由 InternalCacheDiskCacheFactory
创建,最终会通过缓存路径及缓存文件夹最大值创建一个 DiskLruCacheWrapper
对象。
DiskLruCacheWrapper
顾名思义是一个包装类,包装的是 DiskLruCache
。且实现了 DiskCache
接口,该接口定义了磁盘缓存的操作。
另外,内部还持有 SafeKeyGenerator
对象,该对象可以根据 Key
对象得到对应的字符串 key
值,而字符串 key
就是用于索引磁盘中缓存的图片资源的。
以及,在向磁盘写入文件时(put
方法)会使用重入锁来同步代码,也就是 DiskCacheWriteLocker
类,其中主要是对 ReentrantLock
的包装。
参考文章
1、Android图片加载框架最全解析(三),深入探究Glide的缓存机制
2、Glide 源码分析解读-缓存模块-基于最新版Glide 4.9.0