Glide的缓存流程
上一篇讲解了Glide的整体流程,其实很多时候,只有第一次加载图片的时候,我们才会按照那一个流程去走。因为很多时候,我们都是有缓存了。有了缓存之后,加载流程就会稍微变一下了。那么今天,我们就来讲解一下Glide中的缓存。在讲解Glide缓存之后,我建议大家先去了解一下LinkedHashMap
的实现。因为这里涉及到LRU
算法。
先来一张Glide缓存的流程图吧,让大家对Glide的流程有一个印象,方便之后的分析,以下流程图是基于配置了允许缓存的流程,配置了不允许缓存的不在本博客的讨论范围。
Glide缓存流程图
通过上面这个流程图,我们可以知道Glide的缓存可以分为三级,第一个是ActiveResources
,第二个是MemoryCache
,第三个是DiskCache
。后面两个,大家都比较熟悉了,一个是内存缓存,一个是磁盘缓存。
ActiveResources
那么就先简单介绍一个ActiveResources
。先看ActiveResources
的构造函数,以及它里面的一些成员变量
final class ActiveResources {
private final boolean isActiveResourceRetentionAllowed;
private final Executor monitorClearedResourcesExecutor;
@VisibleForTesting final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
private final ReferenceQueue<EngineResource<?>> resourceReferenceQueue = new ReferenceQueue<>();
private ResourceListener listener;
private volatile boolean isShutdown;
@Nullable private volatile DequeuedResourceCallback cb;
ActiveResources(boolean isActiveResourceRetentionAllowed) {
this(
isActiveResourceRetentionAllowed,
java.util.concurrent.Executors.newSingleThreadExecutor(
new ThreadFactory() {
@Override
public Thread newThread(@NonNull final Runnable r) {
return new Thread(
new Runnable() {
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
r.run();
}
},
"glide-active-resources");
}
}));
}
//第一个构造方法最终会调这个构造方法
@VisibleForTesting
ActiveResources(
boolean isActiveResourceRetentionAllowed, Executor monitorClearedResourcesExecutor) {
this.isActiveResourceRetentionAllowed = isActiveResourceRetentionAllowed;
this.monitorClearedResourcesExecutor = monitorClearedResourcesExecutor;
monitorClearedResourcesExecutor.execute(
new Runnable() {
@Override
public void run() {
//调用该方法
cleanReferenceQueue();
}
});
}
@Synthetic
void cleanReferenceQueue() {
//一直循环
while (!isShutdown) {
try {
ResourceWeakReference ref = (ResourceWeakReference) resourceReferenceQueue.remove();
cleanupActiveReference(ref);
// This section for testing only.
DequeuedResourceCallback current = cb;
if (current != null) {
current.onResourceDequeued();
}
// End for testing only.
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
ActiveResources
构建完成后,会启动一个后台优先级级别(THREAD_PRIORITY_BACKGROUND
)的线程,主要就是在while循环里面调用了resourceReferenceQueue
的remove
(),这个方法会一直阻塞当前线程,直到有返回值。当ResourceWeakReference
里面的EngineResource
被内存回收掉的时候才会有返回值。
看一下cleanReferenceQueue
方法:
//这个方法在两处被两用,一个就是上面的cleanReferenceQueue中,还有一个就是get方法中
@Synthetic
void cleanupActiveReference(@NonNull ResourceWeakReference ref) {
synchronized (this) {
activeEngineResources.remove(ref.key);
if (!ref.isCacheable || ref.resource == null) {
return;
}
}
//如果是在get()中调用这个方法会走到这里来
// 回调Engine的onResourceReleased方法
// 这会导致此资源从active变成memory cache状态
EngineResource<?> newResource =
new EngineResource<>(
ref.resource, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ false, ref.key, listener);
listener.onResourceReleased(ref.key, newResource);
}
接着看一下是如何保存和删除Resource
的
//保存resource
synchronized void activate(Key key, EngineResource<?> resource) {
//先将resource封装成ResourceWeakReference
ResourceWeakReference toPut =
new ResourceWeakReference(
key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);
//然后将ResourceWeakReference对象存入队列中
ResourceWeakReference removed = activeEngineResources.put(key, toPut);
//如果之前的队列中有相同的key存在的对象,那么应该将之前的对应重置
if (removed != null) {
removed.reset();
}
}
//删除resource。这里代码简单多了,就不过多的分析了
synchronized void deactivate(Key key) {
ResourceWeakReference removed = activeEngineResources.remove(key);
if (removed != null) {
removed.reset();
}
}
这上面要分析的是关于ResourceWeakReference
//继承了WeakReference
static final class ResourceWeakReference extends WeakReference<EngineResource<?>> {
@SuppressWarnings("WeakerAccess")
@Synthetic
final Key key;
@SuppressWarnings("WeakerAccess")
@Synthetic
final boolean isCacheable;
@Nullable
@SuppressWarnings("WeakerAccess")
@Synthetic
Resource<?> resource;
@Synthetic
@SuppressWarnings("WeakerAccess")
ResourceWeakReference(
@NonNull Key key,
@NonNull EngineResource<?> referent,
@NonNull ReferenceQueue<? super EngineResource<?>> queue,
boolean isActiveResourceRetentionAllowed) {
//注意这个super函数,这样的作用是,如果referent将要被GC,就会被放入queue中。具体请查阅相关的ReferenceQueue的知识点
super(referent, queue);
this.key = Preconditions.checkNotNull(key);
this.resource =
referent.isMemoryCacheable() && isActiveResourceRetentionAllowed
? Preconditions.checkNotNull(referent.getResource())
: null