一、作用
/**
* Generates {@link com.bumptech.glide.load.data.DataFetcher DataFetchers} from original source data
* using registered {@link com.bumptech.glide.load.model.ModelLoader ModelLoaders} and the model
* provided for the load.
*
* <p> Depending on the disk cache strategy, source data may first be written to disk and then
* loaded from the cache file rather than returned directly. </p>
*/
根据官方注释,我们可以看到SourceGenerator用于从用户提供的路径model,获取原始数据data。然后根据磁盘缓存策略,会先缓存在本地,然后再次读取,而不是直接使用原始数据data。
二、源码解析
从Glide4.9.0源码 整体概述(二)可以看到,SourceGenerator
的入口方法是startNext
,所以我们可以先从这个方法入手。
//SourceGenerator.java
SourceGenerator(DecodeHelper<?> helper, FetcherReadyCallback cb) {
this.helper = helper;
this.cb = cb;
}
@Override
public boolean startNext() {
//缓存,第一次执行不会跑到这里。
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
//移交给sourceCacheGenerator处理,第一次执行不会跑到这里
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
//这里需要注意,假如缓存策略认为可以缓存data,那么就不需要管后面的loadPath,直接先把data获取。
//因为缓存之后将会移交给DataCacheGenerator处理,所以可以跳过后面的hasLoadPath判断。
//hasLoadPath是根据Registry中已经注册的解码器,转换器判断是否可以完成dataclass->resourceclass->transcodeclass的变换。
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
private boolean hasNextModelLoader() {
return loadDataListIndex < helper.getLoadData().size();
}
第一次执行startNext
方法,可以看到我们会跑到一个while
循环中执行hasNextModelLoader
方法,那么helper.getLoadData()
是什么呢?建议先看Glide4.9.0 数据转换设计思路
//DecodeHelper
List<LoadData<?>> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = modelLoaders.size(); i < size; i++) {
ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
LoadData<?> current =
modelLoader.buildLoadData(model, width, height, options);
if (current != null) {
loadData.add(current);
}
}
}
return loadData;
}
在getLoadData
方法中会调用getModelLoaders
获取modelLoaders
(Glide4.9.0 getModelLoaders 源码解析),再使用modelLoader#buildLoadData
获取LoadData
。
可以看到LoadData
其实是ModelLoader
接口的一个内部类,LoadData
类中持有一个DataFetcher
接口实例,DataFetcher
接口定义了真正获取数据的loadData
方法。ModelLoader
接口主要是使用handles
方法,判断是否可以处理这个Model
。可以的话就实现了Model
->data
的转换。Glide
内置的ModelLoader
有以下这么多,在调用DataFetcher#loadData
之后会回调SourceGenerator#onDataReady
或者onLoadFailed
。
这里看到了刚开始startNext
方法中的dataToCache
变量,所以就是在这里把获得的data
数据保存,这里的cb其实是DecodeJob
//SourceGenerator .java
@Override
public void onDataReady(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
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.
cb.reschedule();
} else {
cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
loadData.fetcher.getDataSource(), originalKey);
}
}
Decode.java再次回调,Glide4.9.0源码 整体概述(二)中提及了回调顺序,所以这里应该是回调EngineJob
//DecodeJob.java
@Override
public void reschedule() {
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
callback.reschedule(this);
}
只是简单的把DecodeJob再次丢进线程池运行。结合Glide4.9.0源码 整体概述(二)可以知道,这时候将会再次执行SourceGenerator
,就会跑到了前文所说第一次执行不会走的逻辑,缓存并且移交给DataCacheGenerator
处理。
//EngineJob.java
@Override
public void reschedule(DecodeJob<?> job) {
// Even if the job is cancelled here, it still needs to be scheduled so that it can clean itself
// up.
getActiveSourceExecutor().execute(job);
}
可是我们前面并没有看到sourceCacheGenerator 的赋值,其实是在cacheData方法的最后一行。
//SourceGenerator.java
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 = new DataCacheKey(loadData.sourceKey, helper.getSignature());
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();
}
//值得注意的是,这个情况下DataCacheGenerator的回调并不是DecodeJob而是SourceGenerator 。
sourceCacheGenerator =
new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
}
所以可以看到和官方注释的一样,假如设置了缓存原数据,将会存储到本地之后再次读取本地数据,再进行处理。这里也是我不大理解的地方,为什么要读取本地数据,而不是存储之后直接使用内存数据呢?也有可能是由于SourceGenerator#startNext
方法中,在设置了缓存原数据的情况下,判断开始任务的时候,跳过了hasLoadPath的判断。欢迎大家讨论。