Glide最新源码解析(五)-缓存策略-内存缓存
介绍
内存缓存我的理解就是在内存中持久化一份数据,当下次需要的时候直接取出来使用。对于Android设备就两块内存,一块是磁盘,一块是虚拟机中的堆内存,因为堆内存空间大而且是线程共享的。Glide中利用这两块内存做了极大化的缓存,提高数据的访问速度。所以总结缓存策略就是:内存缓存和磁盘缓存。不过在两种缓存的基础上又做出了优化方法。
说明一点:这里缓存管理的都是Resource<T>对象,该对象是对原生资源(baitmap,drawable,gif)的一种包装
EngineResource<T>实现Resource接口,对资源的引用计算,采用引用计数法,当资源被引用的时候计数器+1,当引用不在使用的时候计数器-1,当计数=0的时候,回调释放资源接口,高层模块决定释放的资源怎么处理。EngineResource是一个代理模式(智能引用)代理Resource对象。
内存缓存
Glide在内存缓存中做出的优化是,加入了弱引用缓存,内存缓存和复用池(bitmapArrayPool,byteArrayPool)
弱引用缓存-ActiveResources
ActiveResources缓存ResourceWeakReference对象,该对象弱引用EngineResource对象。
既然是内存缓存(map管理)就一定离不开增,删,查动作,我们看一下这几个动作的处理方式。
先看一下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;//是否终止线程
1.在构造的时候会启动一个核心线程,一直运行监视弱引用队列中的引用对象被gc回收掉,被回收掉的对象就调用cleanupActiveReference方法,从缓存中移除引用,并且调用 listener.onResourceReleased(ref.key, newResource);
ActiveResources(
boolean isActiveResourceRetentionAllowed, Executor monitorClearedResourcesExecutor) {
this.isActiveResourceRetentionAllowed = isActiveResourceRetentionAllowed;
this.monitorClearedResourcesExecutor = monitorClearedResourcesExecutor;
monitorClearedResourcesExecutor.execute(
new Runnable() {
@Override
public void run() {
cleanReferenceQueue();
}
});
}
void cleanReferenceQueue() {
while (!isShutdown) {//1
try {
ResourceWeakReference ref = (ResourceWeakReference) resourceReferenceQueue.remove();
cleanupActiveReference(ref);
...
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
void cleanupActiveReference(@NonNull ResourceWeakReference ref) {
synchronized (this) {
activeEngineResources.remove(ref.key);
if (!ref.isCacheable || ref.resource == null) {
return;
}
}
EngineResource<?> newResource =
new EngineResource<>(
ref.resource, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ false, ref.key, listener);
listener.onResourceReleased(ref.key, newResource);//1.释放资源
}
这里有一个疑问:resourceReferenceQueue是一个阻塞队列,remove方法的时候,没有数据的时候会调用
lock.wait(timeout);挂起线程等待唤醒,所以我感觉不需要while死循环(注释1)。后来请教了Alan老师,才恍然大悟,这个队列是所有ResourceWeakReference对象共享的一个队列,所以需要一直循环监听对象回收的情况。 我们看一下释放的资源怎么处理?实现的地方在Engine中
public class Engine
implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
...
@Override
public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
activeResources.deactivate(cacheKey);
if (resource.isMemoryCacheable()) {//支持缓存,放入缓存中
cache.put(cacheKey, resource);
} else {//不支持缓存就回收掉
resourceRecycler.recycle(resource);
}
}
...
释放掉的资源放入内存缓存中。
2.添加资源
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();
}
}
注意这里new ResourceWeakReference的时候,传入的是公用的一个resourceReferenceQueue队列。我们看一下这个类的结构
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, queue);
this.key = Preconditions.checkNotNull(key);
this.resource =
referent.isMemoryCacheable() && isActiveResourceRetentionAllowed
? Preconditions.checkNotNull(referent.getResource())
: null;
isCacheable = referent.isMemoryCacheable();
}
void reset() {
resource = null;
clear();
}
}
这里使用强引用引用了resource对象,以供后续资源释放的时候使用
2.移除资源
synchronized void deactivate(Key key) {
ResourceWeakReference removed = activeEngineResources.remove(key);
if (removed != null) {
removed.reset();
}
}
3.获取资源
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;
}
总结
ActiveResources缓存的是Resource的弱引用,并且监听Resource被GC回收掉,被回收掉的弱引用使用listener.onResourceReleased(ref.key, newResource);处理,从map主动移除的弱引用都调用reset()释放掉引用的resource
内存缓存
先看一下内存缓存的接口结构
/** 内存缓存中添加和移除资源的接口 */
public interface MemoryCache {
/** 无论什么时候当bitmap从缓存中移出去的时候回调的接口(这指的移除指的是当内存溢出的时候,要溢出资源来容纳其他资源) */
interface ResourceRemovedListener {
void onResourceRemoved(@NonNull Resource<?> removed);
}
/** 查询缓存中所有内容的大小,单位是byte */
long getCurrentSize();
/** 内存最大的容量 */
long getMaxSize();
/**
* 调整内存大小
* <p>If the size multiplier causes the size of the cache to be decreased, items will be evicted
* until the cache is smaller than the new size.
*
* @param multiplier A size multiplier >= 0.
*/
void setSizeMultiplier(float multiplier);
/**
* 根据key移除资源,返回移除的资源
* @param key The key.
*/
@Nullable
Resource<?> remove(@NonNull Key key);
/**
* 添加资源
* @param key The key to retrieve the bitmap.
* @param resource The {@link com.bumptech.glide.load.engine.EngineResource} to store.
* @return The old value of key (null if key is not in map).
*/
@Nullable
Resource<?> put(@NonNull Key key, @Nullable Resource<?> resource);
/**
* 资源移除回调
* @param listener The listener.
*/
void setResourceRemovedListener(@NonNull ResourceRemovedListener listener);
/** 清空内存中所有的数据 */
void clearMemory();
/**
* 根据不同的级别调整内存大小
* @param level This integer represents a trim level as specified in {@link
* android.content.ComponentCallbacks2}.
*/
void trimMemory(int level);
}
接口的实现类有两个一个是MemoryCacheAdapter 适配不适用内存缓存的时候,一个是LruResourceCache
我们主要分析LruResourceCache.java,顾名思义采用的是LRU算法缓存的资源。
public class LruResourceCache extends LruCache<Key, Resource<?>> implements MemoryCache {
private ResourceRemovedListener listener;
....
@Override
public void setResourceRemovedListener(@NonNull ResourceRemovedListener listener) {
this.listener = listener;
}
@Override
protected void onItemEvicted(@NonNull Key key, @Nullable Resource<?> item) {
if (listener != null && item != null) {
listener.onResourceRemoved(item);//1资源从内存中移除
}
}
....
}
这里采用的是类适配器模式,LruCache中的put get方法去适配MemoryCache 接口中定义的抽象方法。Glide中的LruCache没有采用Android系统的LruCache类,而是自己实现了一个LruCache类,其中主要区别是Glide中的onItemEvicted方法只有在put和trimToSize时候调用。
注释1处资源从内存中移出去的回调,实现该回调的地方是Engine类。
public class Engine
implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
...
@Override
public void onResourceRemoved(@NonNull final Resource<?> resource) {
//回收资源
resourceRecycler.recycle(resource);
}
...
}
ResourceRecycler类中会按照顺序安全的回收每一个资源,即调用resource.recycle();方法,我们默认用的是BitmapResuource实例,我们看一下BitmapResuource中的recycle方法。
public class BitmapResource implements Resource<Bitmap>, Initializable {
...
@Override
public void recycle() {
bitmapPool.put(bitmap);
}
...
}
被回收掉的bitmap放到了复用池中。小记:内存缓存中当内存溢出的时候,会清理资源腾出空间,以满足其他资源的加入,清理掉的资源会被放入复用池中。