问题
- Glide分别有哪些缓存
- Glide在什么时候会从缓存中读取数据
- Glide在什么时候会从存入缓存
Glide有哪些缓存
- 弱引用缓存ActiveResources
- Lru缓存LruResourceCache
- 硬盘缓存DiskLruCache
如何从缓存中拉取数据
内存缓存
在SingleRequest发起任务时
public <R> LoadStatus load(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class<?> resourceClass,
Class<R> transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map<Class<?>, Transformation<?>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb,
Executor callbackExecutor) {
//根据model、自定义的signature、宽高、变形等参数生成一个key用于识别这次load请求
EngineKey key =
keyFactory.buildKey(
model,
signature,
width,
height,
transformations,
resourceClass,
transcodeClass,
options);
EngineResource<?> memoryResource;
synchronized (this) {
//从内存中加载数据
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
if (memoryResource == null) {
//没有缓存的情况
return waitForExistingOrStartNewJob(
glideContext,
model,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
options,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache,
cb,
callbackExecutor,
key,
startTime);
}
}
// 有缓存就直接回调onResourceReady了,都不用放线程池处理
cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
return null;
}
private EngineResource<?> loadFromMemory(
EngineKey key, boolean isMemoryCacheable, long startTime) {
if (!isMemoryCacheable) {
return null;
}
//从ActiveResources中获取
EngineResource<?> active = loadFromActiveResources(key);
if (active != null) {
return active;
}
//从MemoryCache中获取,默认是LruResourceCache
EngineResource<?> cached = loadFromCache(key);
if (cached != null) {
return cached;
}
return null;
}
//根据key从activeResources对象中获取,它是一个Map<Key, ResourceWeakReference>
private EngineResource<?> loadFromActiveResources(Key key) {
EngineResource<?> active = activeResources.get(key);
if (active != null) {
//active的被引用数+1,在想要release的时候只有等于0才会真的去释放
active.acquire();
}
return active;
}
private EngineResource<?> loadFromCache(Key key) {
//以remove的方式拿数据,即拿到了会直接从LruCache中删除并去除该对象
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
//同样被引用数+1
cached.acquire();
//若从lruCache中获取到了所需数据,则
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, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ true, key, /*listener=*/ this);
}
return result;
}
总结:
- 在正式通过线程池开启任务之前,会先尝试从内存拉取缓存
- 缓存的key为EngineKey,包含了model、size、options等多个属性
- 先从ActiveResource中获取,它存的是当前正在使用的Resource
- 再尝试从LruResourceCache中获取,它存的是当前未使用的Resource(因为它所包含的Resource一使用就会被remove掉插入ActiveResource中)
硬盘缓存
在DecodeJob执行时,有一个获取下一步任务的方法
//DecodeJob.java
private Stage getNextStage(Stage current) {
switch (current) {
case INITIALIZE:
return diskCacheStrategy.decodeCachedResource()
? Stage.RESOURCE_CACHE
: getNextStage(Stage.RESOURCE_CACHE);
case RESOURCE_CACHE:
return diskCacheStrategy.decodeCachedData()
? Stage.DATA_CACHE
: getNextStage(Stage.DATA_CACHE);
case DATA_CACHE:
// Skip loading from source if the user opted to only retrieve the resource from cache.
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}
//RESOURCE_CACHE和DATA_CACHE就对应的是转换过大小后的最终图片和源图片
private DataFetcherGenerator getNextGenerator() {
switch (stage) {
case RESOURCE_CACHE:
return new ResourceCacheGenerator(decodeHelper, this);
case DATA_CACHE:
return new DataCacheGenerator(decodeHelper, this);
case SOURCE:
return new SourceGenerator(decodeHelper, this);
case FINISHED:
return null;
default:
throw new IllegalStateException("Unrecognized stage: " + stage);
}
}
所以他们分别对应了ResourceCacheGenerator和DataCacheGenerator,以ResourceCacheGenerator为例
public boolean startNext() {
//遍历最终可能生成的Resource类,如Bitmap、GifDrawable、BitmapDrawable
while (modelLoaders == null || !hasNextModelLoader()) {
resourceClassIndex++;
if (resourceClassIndex >= resourceClasses.size()) {
sourceIdIndex++;
if (sourceIdIndex >= sourceIds.size()) {
return false;
}
resourceClassIndex = 0;
}
Key sourceId = sourceIds.get(sourceIdIndex);
Class<?> resourceClass = resourceClasses.get(resourceClassIndex);
Transformation<?> transformation = helper.getTransformation(resourceClass);
//与内存的key类似,通过宽高等属性生成一个硬盘缓存key
currentKey =
new ResourceCacheKey( // NOPMD AvoidInstantiatingObjectsInLoops
helper.getArrayPool(),
sourceId,
helper.getSignature(),
helper.getWidth(),
helper.getHeight(),
transformation,
resourceClass,
helper.getOptions());
//从DiskLruCache中获取对应文件
cacheFile = helper.getDiskCache().get(currentKey);
if (cacheFile != null) {
sourceKey = sourceId;
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}
loadData = null;
boolean started = false;
//获取缓存成功,则接下来通过ModelLoader去加载
while (!started && hasNextModelLoader()) {
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
loadData =
modelLoader.buildLoadData(
cacheFile, helper.getWidth(), helper.getHeight(), helper.getOptions());
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
我们可以在Glide初始化时找到输入为File的ModelLoader
//Glide.java
.append(File.class, ByteBuffer.class, new ByteBufferFileLoader.Factory())
它会把通过ByteBufferUtil把File转化为ByteBuffer
//ByteBufferFileLoader.java
public void loadData(
@NonNull Priority priority, @NonNull DataCallback<? super ByteBuffer> callback) {
ByteBuffer result;
try {
//这里实现的File到ByteBuffer的转换
result = ByteBufferUtil.fromFile(file);
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to obtain ByteBuffer for file", e);
}
callback.onLoadFailed(e);
return;
}
callback.onDataReady(result);
}
//ByteBufferUtil.java
public static ByteBuffer fromFile(@NonNull File file) throws IOException {
RandomAccessFile raf = null;
FileChannel channel = null;
try {
long fileLength = file.length();
// See #2240.
if (fileLength > Integer.MAX_VALUE) {
throw new IOException("File too large to map into memory");
}
// See b/67710449.
if (fileLength == 0) {
throw new IOException("File unsuitable for memory mapping");
}
raf = new RandomAccessFile(file, "r");
channel = raf.getChannel();
return channel.map(FileChannel.MapMode.READ_ONLY, 0, fileLength).load();
} finally {
if (channel != null) {
try {
channel.close();
} catch (IOException e) {
// Ignored.
}
}
if (raf != null) {
try {
raf.close();
} catch (IOException e) {
// Ignored.
}
}
}
}
而DataCacheGenerator的实现方式也是通过DiskLruCache实现的,但是它的key则没有ResourceCacheGenerator的key构建那么复杂,只是根据传入的model和signature合并起来获得key而已,所以就不再赘述了。
以上就是缓存的读取部分
何时往缓存中保存数据
内存缓存
在数据被转化成最终的Drawable数据之后,EncodeJob会收到onEngineJobComplete回调
//EncodeJob.java
public synchronized void onEngineJobComplete(
EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
// A null resource indicates that the load failed, usually due to an exception.
if (resource != null && resource.isMemoryCacheable()) {
//把数据存入activeResources中
activeResources.activate(key, resource);
}
jobs.removeIfCurrent(key, engineJob);
}
而对于LruResourceCache,它的put方法会在一个Resource被调用release时调用
//EngineResource.java
void release() {
boolean release = false;
synchronized (this) {
if (acquired <= 0) {
throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
}
if (--acquired == 0) {
release = true;
}
}
if (release) {
//若被引用数为0,那么调用Engine的onResourceReleased
listener.onResourceReleased(key, this);
}
}
public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
//从activeResources移除对应的弱引用
activeResources.deactivate(cacheKey);
if (resource.isMemoryCacheable()) {
//存到LRUResourceCache中
cache.put(cacheKey, resource);
} else {
resourceRecycler.recycle(resource);
}
}
所以内存缓存的存储时机如下
- 一个数据加载成功之后,会存入ActiveResource中
- 一个Resource的被引用数为0之后,会存入LruResourceCache中
硬盘缓存
源数据Data缓存
在SourceGenerator成功加载出数据之后,会回调到onDataReady
//SourceGenerator.java
public void onDataReady(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
//若要保存Data数据,就在此处了
dataToCache = data;
// We might be being called back on someone else's thread. Before doing anything, we should
// reschedule to get back onto Glide's thread.
//重新触发调用EncodeJob执行同一个DecodeJob
cb.reschedule();
} else {
cb.onDataFetcherReady(
loadData.sourceKey,
data,
loadData.fetcher,
loadData.fetcher.getDataSource(),
originalKey);
}
}
//DecodeJob.java
public void reschedule() {
//调用到decode之前,把runReason改为SWITCH_TO_SOURCE_SERVICE
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
callback.reschedule(this);
}
//EncodeJob.java
public void reschedule(DecodeJob<?> job) {
//这里再去重新执行DecodeJob
getActiveSourceExecutor().execute(job);
}
//DeocdeJob.java
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
stage = getNextStage(Stage.INITIALIZE);
currentGenerator = getNextGenerator();
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
//这次走这里,此时的currentGenerator还是SourceGenerator
runGenerators();
break;
case DECODE_DATA:
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
//SourceGenerator.java
@Override
//转了一圈,重新回到了这里
public boolean startNext() {
//dataToCache在本小节最初就赋值了,就是我们得到的源数据
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
//这里就是关键方法cacheData
cacheData(data);
}
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
sourceCacheGenerator = null;
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
private void cacheData(Object dataToCache) {
long startTime = LogTime.getLogTime();
try {
Encoder<Object> encoder = helper.getSourceEncoder(dataToCache);
DataCacheWriter<Object> writer =
new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());
//与获取时一样,保存直接用的originalKey
originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
//存入DiskLruCache中
helper.getDiskCache().put(originalKey, writer);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(
TAG,
"Finished encoding source to cache"
+ ", key: "
+ originalKey
+ ", data: "
+ dataToCache
+ ", encoder: "
+ encoder
+ ", duration: "
+ LogTime.getElapsedMillis(startTime));
}
} finally {
loadData.fetcher.cleanup();
}
//当前要运行的generator改为DataCacheGenerator,之后就会从这个对象里拉取数据回调到onLoadDataReady
sourceCacheGenerator =
new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
}
上面就是源数据Data最终存储到DiskLruCache中的过程,会在SourceGenerator加载完成源数据(如InputStream)后保存
最终数据Resource缓存
最终数据Resource会在DecodeJob加载完成后回调成功时生成
即onDataFetchReady之后会调用到decode的步骤
//DecodeJob.java
private void decodeFromRetrievedData() {
Resource<R> resource = null;
try {
//这里去通过Decoder把InputStream转化为BitmapDrawable之后
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
//回调最终数据resource已生成
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
Resource<R> result = resource;
LockedResource<R> lockedResource = null;
if (deferredEncodeManager.hasResourceToEncode()) {
lockedResource = LockedResource.obtain(resource);
result = lockedResource;
}
notifyComplete(result, dataSource);
stage = Stage.ENCODE;
try {
if (deferredEncodeManager.hasResourceToEncode()) {
//注意这里就是去编码数据,把数据缓存到DiskLruCache
deferredEncodeManager.encode(diskCacheProvider, options);
}
} finally {
if (lockedResource != null) {
lockedResource.unlock();
}
}
onEncodeComplete();
}
void encode(DiskCacheProvider diskCacheProvider, Options options) {
GlideTrace.beginSection("DecodeJob.encode");
try {
//getDiskCache就是DiskLruCache了,它会通过各种encoder去写数据
//而Encoder其实也和Decoder类似是通过Glide初始化时实现的
diskCacheProvider
.getDiskCache()
.put(key, new DataCacheWriter<>(encoder, toEncode, options));
} finally {
toEncode.unlock();
GlideTrace.endSection();
}
}
Glide自带的Encoder类
//Glide.java
.append(BitmapDrawable.class, new BitmapDrawableEncoder(bitmapPool, bitmapEncoder))
.append(GifDrawable.class, new GifDrawableEncoder())
.append(Bitmap.class, bitmapEncoder)
总结
Glide分别有哪些缓存
分别有
- 弱引用ActiveResource(正在使用的)
- 内存缓存LruCache
- 硬盘缓存DiskLruCache(包括Data和Resource)
Glide在什么时候会从缓存中读取数据
一次加载开始
- 从ActiveResource拉取Resource
- 再从LruCache中拉取Resource
- 最后从DiskLruCache中拉取
- 以上都没有,自己去加载
Glide在什么时候会从存入缓存
- 一个Resource加载完成后直接存入ActiveResource
- 一个Resource完全没有target使用后存入LruCache
- 成功加载出Data(如InputStream)后,存入DiskLruCache的Data
- decode加载结束生成了最终Resource后,存入DiskLruCache的Resource