前言
在之前我们看Glide获取数据的时候,第一个就是从ActiveResource中获取的,作为第一级缓存,那么它究竟是个什么东西,下面让我们来揭开它的神秘面纱。
第一级缓存
这里的代码很简单,从ActiveResource中根据key获取EngineResource,由此我们可以猜测ActiveResource很有可能是由Map来保存数据的。
//Engine.load()
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
return null;
}
private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> active = activeResources.get(key);
if (active != null) {
active.acquire();
}
return active;
}
ActiveResource源码
在构造函数中对变量isActiveResourceRetentionAllowed和monitorClearedResourcesExecutor赋值,在Engine中调用的是它的第一个构造函数,那么它实际使用的是一个只有一个线程的线程池,并且设置优先级为后台线程,然后就开始执行cleanReferenceQueue()。
其次用HashMap保存了ResourceWeakReference,也证实了上面的猜想。ResourceWeakReference这个类在下面会讲到。
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;
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();
}
});
}
}
isActiveResourceRetentionAllowed代表是否保留活动资源,可以通过GlideBuilder赋值,默认为false。如果设置为true则Glide会持有底层的资源(比如Bitmap)的强引用用来做内存缓存。
//GlideBuilder
private boolean isActiveResourceRetentionAllowed;
/**
* Defaults to {@code false}.
*/
public GlideBuilder setIsActiveResourceRetentionAllowed(
boolean isActiveResourceRetentionAllowed) {
this.isActiveResourceRetentionAllowed = isActiveResourceRetentionAllowed;
return this;
}
在看cleanReferenceQueue()之前先来看下ActiveResource的静态内部类ResourceWeakReference,稍后会用到。
ResourceWeakReference
这里继承了WeakReference来持有EngineResource,这样当只有弱引用持有EngineResource的时候如果发生了gc则会回收掉EngineResource。
同时将ActiveResource的ReferenceQueue传入,这样当EngineResource被回收时就能知道了。
默认情况下变量resource的值为null,isCacheable的值为true。
static final class ResourceWeakReference extends WeakReference<EngineResource<?>> {
@Synthetic
final Key key;
@Synthetic
final boolean isCacheable;
@Nullable
@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(); //BaseRequestOptions中的值默认为true
}
//清除资源
void reset() {
resource = null;
clear();
}
看完ResourceWeakReference之后接下来开始看cleanReferenceQueue(),主要就是在while循环里面调用了resourceReferenceQueue的remove(),这个方法会一直阻塞当前线程,直到有返回值。当ResourceWeakReference里面的EngineResource被内存回收掉的时候才会有返回值,所以这里用线程池开了一个线程来处理。接着执行cleanupActiveReference(),把HashMap中保存的ResourceWeakReference删除。如果在GlideBuilder中设置了isActiveResourceRetentionAllowed为true则会接着执行下面的方法,把实际的资源重新生成一个EngineResource,并回调给Engin让它存入MemoryCache中。
void cleanReferenceQueue() {
while (!isShutdown) {
try {
ResourceWeakReference ref = (ResourceWeakReference) resourceReferenceQueue.remove();
cleanupActiveReference(ref);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
void cleanupActiveReference(@NonNull ResourceWeakReference ref) {
synchronized (listener) {
synchronized (this) {
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);
}
}
}
//Engine
public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
activeResources.deactivate(cacheKey);
if (resource.isCacheable()) {
cache.put(cacheKey, resource); //MemoryCache
} else {
resourceRecycler.recycle(resource);
}
}
//ActiveResources
synchronized void deactivate(Key key) {
ResourceWeakReference removed = activeEngineResources.remove(key);
if (removed != null) {
removed.reset();
}
}
看完了监听内存回收的逻辑,接下来看下是如何从ActiveResource获取EngineResource的。直接从HashMap中根据key取ResourceWeakReference,如果没被回收则再取里面的EngineResource,如果已经被回收了则执行清除工作。
synchronized 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;
}
ActiveResource并没有提供put()来保存数据,取而代之的是activate()。在Engine的load()中如果一开始在ActiveResource没有获取到EngineResource,则接下来会在MemoryCache中获取,如果获取到则会把EnginResource保存到ActiveResource。
//Engine
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;
}
synchronized void activate(Key key, EngineResource<?> resource) {
ResourceWeakReference toPut =
new ResourceWeakReference(
key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);
ResourceWeakReference removed = activeEngineResources.put(key, toPut);
if (removed != null) {
removed.reset();
}
}
总结
ActiveResource做为Glide的第一级缓存,保存的是那些活跃的EngineResource,即没有被内存回收的数据。这里对缓存的大小没有限制,防止因为同时加载的图片太多造成了MemoryCache因为大小限制而移出缓存,导致最终去使用磁盘缓存的问题。同时使用了弱引用,保证了当进行内存回收时能及时回收掉,避免一直占用内存。