Glide使用了DiskCache+MemoryCache+activeResources三级缓存
DiskCache是磁盘缓存,这个不用多说了
MemoryCache和activeResources是两级内存缓存
MemoryCache存储当前没有使用到的Bitmap,当MemoryCache中命中Bitmap后,该Bitmap又被缓存至activeResources中,并从MemoryCache中删除
private EngineResource> loadFromCache(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
EngineResource> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
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;
}
activeResources可以理解为高速缓存,存储当前正在使用的Bitmap,并且使用weakReference+ReferenceQueue的方式保存,发生Gc的时候,又被缓存至MemoryCache中
下面是ActiveResources类
package com.bumptech.glide.load.engine;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import com.bumptech.glide.load.Key;
import com.bumptech.glide.load.engine.EngineResource.ResourceListener;
import com.bumptech.glide.util.Preconditions;
import com.bumptech.glide.util.Synthetic;
import com.bumptech.glide.util.Util;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
final class ActiveResources {
private static final int MSG_CLEAN_REF = 1;
private final boolean isActiveResourceRetentionAllowed;
private final Handler mainHandler = new Handler(Looper.getMainLooper(), new Callback() {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == MSG_CLEAN_REF) {
cleanupActiveReference((ResourceWeakReference) msg.obj);
return true;
}
return false;
}
});
@VisibleForTesting
final Map activeEngineResources = new HashMap<>();
private ResourceListener listener;
/**
* Lazily instantiate to avoid exceptions if Glide is initialized on a background thread.
*
* @see #295
*/
@Nullable
private ReferenceQueue> resourceReferenceQueue;
@Nullable
private Thread cleanReferenceQueueThread;
private volatile boolean isShutdown;
@Nullable
private volatile DequeuedResourceCallback cb;
ActiveResources(boolean isActiveResourceRetentionAllowed) {
this.isActiveResourceRetentionAllowed = isActiveResourceRetentionAllowed;
}
void setListener(ResourceListener listener) {
this.listener = listener;
}
void activate(Key key, EngineResource> resource) {
ResourceWeakReference toPut =
new ResourceWeakReference(
key,
resource,
getReferenceQueue(),
isActiveResourceRetentionAllowed);
ResourceWeakReference removed = activeEngineResources.put(key, toPut);
if (removed != null) {
removed.reset();
}
}
void deactivate(Key key) {
ResourceWeakReference removed = activeEngineResources.remove(key);
if (removed != null) {
removed.reset();
}
}
@Nullable
EngineResource> get(Key key) {
ResourceWeakReference activeRef = activeEngineResources.get(key);
if (activeRef == null) {
return null;
}
EngineResource> active = activeRef.get();
if (active == null) {
cleanupActiveReference(activeRef);
}
return active;
}
@SuppressWarnings("WeakerAccess")
@Synthetic void cleanupActiveReference(@NonNull ResourceWeakReference ref) {
Util.assertMainThread();
activeEngineResources.remove(ref.key);
if (!ref.isCacheable || ref.resource == null) {
return;
}
EngineResource> newResource =
new EngineResource<>(ref.resource, /*isCacheable=*/ true, /*isRecyclable=*/ false);
newResource.setResourceListener(ref.key, listener);
listener.onResourceReleased(ref.key, newResource);
}
private ReferenceQueue> getReferenceQueue() {
if (resourceReferenceQueue == null) {
resourceReferenceQueue = new ReferenceQueue<>();
cleanReferenceQueueThread = new Thread(new Runnable() {
@SuppressWarnings("InfiniteLoopStatement")
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
cleanReferenceQueue();
}
}, "glide-active-resources");
cleanReferenceQueueThread.start();
}
return resourceReferenceQueue;
}
@SuppressWarnings("WeakerAccess")
@Synthetic void cleanReferenceQueue() {
while (!isShutdown) {
try {
ResourceWeakReference ref = (ResourceWeakReference) resourceReferenceQueue.remove();
mainHandler.obtainMessage(MSG_CLEAN_REF, ref).sendToTarget();
// This section for testing only.
DequeuedResourceCallback current = cb;
if (current != null) {
current.onResourceDequeued();
}
// End for testing only.
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
@VisibleForTesting
void setDequeuedResourceCallback(DequeuedResourceCallback cb) {
this.cb = cb;
}
@VisibleForTesting
interface DequeuedResourceCallback {
void onResourceDequeued();
}
@VisibleForTesting
void shutdown() {
isShutdown = true;
if (cleanReferenceQueueThread == null) {
return;
}
cleanReferenceQueueThread.interrupt();
try {
cleanReferenceQueueThread.join(TimeUnit.SECONDS.toMillis(5));
if (cleanReferenceQueueThread.isAlive()) {
throw new RuntimeException("Failed to join in time");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
@VisibleForTesting
static final class ResourceWeakReference extends WeakReference> {
@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, queue);
this.key = Preconditions.checkNotNull(key);
this.resource =
referent.isCacheable() && isActiveResourceRetentionAllowed
? Preconditions.checkNotNull(referent.getResource()) : null;
isCacheable = referent.isCacheable();
}
void reset() {
resource = null;
clear();
}
}
}
首先看activate方法,这里调用getReferenceQueue方法创建了ReferenceQueue用于绑定弱引用,并且起了一个线程,这个线程不断扫描resourceReferenceQueue,如果队列中有引用,根据ReferenceQueue(Leakcanary中也使用了)的特性,说明发生了GC,这个时候会进入到Handler中处理MSG_CLEAN_REF消息,最后执行到cleanupActiveReference方法中,执行了listener.onResourceReleased(ref.key, newResource);这里进入到Engine类中,执行方法onResourceReleased:
@Override
public void onResourceReleased(Key cacheKey, EngineResource> resource) {
Util.assertMainThread();
activeResources.deactivate(cacheKey);
if (resource.isCacheable()) {
cache.put(cacheKey, resource);
} else {
resourceRecycler.recycle(resource);
}
}
将Bitmap又放进了MemoryCache中去。
至于为什么这么设计,个人理解为MemoryCache缓存的数据可能会比较多,而activeResources只存储当前正在使用的Bitmap,数据存储量比较小,可以很快的进行存取,可以理解为高速缓存,当不被使用的时候又被放入MemoryCache中。
参考文献:
深入探究Glide的缓存机制
https://blog.csdn.net/guolin_blog/article/details/54895665
Glide如何通过引用计数复用Bitmap
https://www.jianshu.com/p/00540c9a4de9