承接上文,我们简单了解了Glide加载图片的流程,在这篇博文中,我们就来了解下Glide是如何缓存图片的。
在上篇博文中我们知道,在初始化Glide
对象时,GlideBuilder
为我们配置了默认的缓存机制:
Glide createGlide() {
if (sourceService == null) {
final int cores = Math.max(1, Runtime.getRuntime().availableProcessors());
sourceService = new FifoPriorityThreadPoolExecutor(cores);
}
if (diskCacheService == null) {
diskCacheService = new FifoPriorityThreadPoolExecutor(1);
}
MemorySizeCalculator calculator = new MemorySizeCalculator(context);
if (bitmapPool == null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
int size = calculator.getBitmapPoolSize();
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
}
if (memoryCache == null) {
memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());
}
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
if (engine == null) {
engine = new Engine(memoryCache, diskCacheFactory, diskCacheService, sourceService);
}
if (decodeFormat == null) {
decodeFormat = DecodeFormat.DEFAULT;
}
return new Glide(engine, memoryCache, bitmapPool, context, decodeFormat);
}
代码清单2-1
通过上面的代码,我们可以了解到,Glide 使用了二级缓存机制。接下来我们就从图片的“从无到有”的过程来分析下具体的缓存流程。
一、缓存图片
在上篇博文中,我们了解到,我们是在DecodeJob
的decodeSource
方法中拿到图片流资源。紧接着调用其decodeFromSourceData
方法:
private Resource<T> decodeFromSourceData(A data) throws IOException {
final Resource<T> decoded;
if (diskCacheStrategy.cacheSource()) {
decoded = cacheAndDecodeSourceData(data);
} else {
long startTime = LogTime.getLogTime();
decoded = loadProvider.getSourceDecoder().decode(data, width, height);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded from source", startTime);
}
}
return decoded;
}
方法中diskCacheStrategy
是在GenericRequestBuilder
初始化的,其值默认为DiskCacheStrategy.RESULT
,即只缓存变换后的图片,而不直接保存原始资源到缓存。故上述方法中并没有保存原始的数据留到缓存中。
那么第一次写入缓存是什么时候呢?我们沿着代码继续往下看,待获取到decoded
资源后,会在decodeFromSource
中调用transformEncodeAndTranscode
方法(不明白调用流程的可参考上篇博文):
private void writeTransformedToCache(Resource<T> transformed) {
if (transformed == null || !diskCacheStrategy.cacheResult()) {
return;
}
long startTime = LogTime.getLogTime();
SourceWriter<Resource<T>> writer = new SourceWriter<Resource<T>>(loadProvider.getEncoder(), transformed);
diskCacheProvider.getDiskCache().put(resultKey, writer);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Wrote transformed from source to cache", startTime);
}
}
在transformEncodeAndTranscode
方法中会将变换后的数据流(Resource<GifBitmapWrapper>
对象)写入磁盘中,并将数据流转码为GlideBitmapDrawableResource
。
由上篇博文我们知道,在拿到数据对象GlideBitmapDrawableResource
后,经过一系列的方法调用,会走到EngineJob
的handleResultOnMainThread
方法中。在该方法中,会将我们原来的数据对象包装为EngineResource
对象。这个对象使用了引用计数 方法对资源进行了控制。当有其他对象方法该资源时会增加计数,反之则减小引用次数。当引用次数减小至0时,则会回调ResourceListener
的onResourceReleased
方法。
同时,在handleResultOnMainThread
方法中,会通过回调通知Engine
EngineJob 已完成:
listener.onEngineJobComplete(key, engineResource);
现在我们来看看Engine
的onEngineJobComplete
方法:
@SuppressWarnings("unchecked")
@Override
public void onEngineJobComplete(Key key, EngineResource<?> resource) {
Util.assertMainThread();
// A null resource indicates that the load failed, usually due to an exception.
if (resource != null) {
resource.setResourceListener(key, this);
if (resource.isCacheable()) {
activeResources.put(key, new ResourceWeakReference(key, resource, getReferenceQueue()));
}
}
// TODO: should this check that the engine job is still current?
jobs.remove(key);
}
在这个方法中为EngineResource
设置了回调接口,并在activeResources
添加了一个资源的若引用。
在EngineResource
的引用计数为0时会执行如下方法:
@Override
public void onResourceReleased(Key cacheKey, EngineResource resource) {
Util.assertMainThread();
activeResources.remove(cacheKey);
if (resource.isCacheable()) {
cache.put(cacheKey, resource);
} else {
resourceRecycler.recycle(resource);
}
}
该方法将资源从activeResources
移除,并放到内存缓存cache
中。
至此,图片的缓存流程已经分析结束,接下来我们看看缓存的使用。
二、读取缓存
缓存的读取主要发生在Engine
中的load
方法中,其读取的顺序依次为Memory cache、activeResources
表、DiskCache 。相信通过前面的流程分析,读取缓存的流程就很容易了。
三、小结
Glide 使用的是二级缓存机制,并引入了引用计数 机制。朋友们可以在阅读源码的过程中慢慢体会其设计的精妙之处。