缓存机制概述
一般来说,缓存经常分为这样的三级
- 内存缓存:优先加载,速度最快
- 本地缓存:次优先加载,速度快
- 网络缓存:最后加载,速度慢,需要网络
一般来说,我们会先通过网络将图片下载到本地,然后加载到内存当中。一旦图片需要再次加载,我们最先先去内存缓存中读取图片。当内存中没有图片缓存的时候,我们再去本地读取。只有当本地也没有图片的时候才会重新向网络进行请求再次加载图片
Glide也沿用这一套机制。不过如果将网络缓存不看作Glide内部的缓存机制的话,我们可以将其划分为两种类型的缓存机制
- 一种是二级缓存即本地缓存和内存缓存
- 一种是三级缓存,也就是在内存缓存中再进行划分为LruCache内存缓存(最近最少缓存策略)和WeakReference内存缓存(弱引用缓存策略),而本地缓存因为只有DiskLruCache硬盘缓存(硬盘缓存策略)
具体缓存图示可见
简单介绍一下这些缓存类型
- DiskLruCache硬盘缓存: 外部缓存,例如可以将网络下载的图片永久的缓存到手机外部存储中去,并可以将缓存数据取出来使用
- LruCache内存缓存:优先淘汰那些近期最少使用的缓存图片对象
- WeakReference内存缓存:“弱引用”,即在引用图片对象的同时仍然允许对该对象进行垃圾回收
其中内存缓存的主要作用是防止应用重复将图片数据读取到内存当中。而硬盘缓存的主要作用是防止应用重复从网络或其他地方下载和读取数据
缓存流程
1. 生成缓存标识key
Engine.java
public LoadStatus load(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class resourceClass,
Class transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map, Transformation> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb,
Executor callbackExecutor) {
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
EngineKey key =
keyFactory.buildKey(
model,
signature,
width,
height,
transformations,
resourceClass,
transcodeClass,
options);
EngineResource memoryResource;
synchronized (this) {
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
···
// Avoid calling back while holding the engine lock, doing so makes it easier for callers to
// deadlock.
cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
return null;
}
load方法是有很多参数的,里面创建了EngineKey的对象key,使用keyFactory的buildKey方法
EngineKeyFactory.java
class EngineKeyFactory {
@SuppressWarnings("rawtypes")
EngineKey buildKey(
Object model,
Key signature,
int width,
int height,
Map, Transformation> transformations,
Class resourceClass,
Class transcodeClass,
Options options) {
return new EngineKey(
model, signature, width, height, transformations, resourceClass, transcodeClass, options);
}
}
返回了一个EngineKey对象,点击查看
EngineKey.java
class EngineKey implements Key {
private final Object model;
private final int width;
private final int height;
private final Class resourceClass;
private final Class transcodeClass;
private final Key signature;
private final Map, Transformation> transformations;
private final Options options;
private int hashCode;
EngineKey(
Object model,
Key signature,
int width,
int height,
Map, Transformation> transformations,
Class resourceClass,
Class transcodeClass,
Options options) {
this.model = Preconditions.checkNotNull(model);
this.signature = Preconditions.checkNotNull(signature, "Signature must not be null");
this.width = width;
this.height = height;
this.transformations = Preconditions.checkNotNull(transformations);
this.resourceClass =
Preconditions.checkNotNull(resourceClass, "Resource class must not be null");
this.transcodeClass =
Preconditions.checkNotNull(transcodeClass, "Transcode class must not be null");
this.options = Preconditions.checkNotNull(options);
}
@Override
public boolean equals(Object o) {
if (o instanceof EngineKey) {
EngineKey other = (EngineKey) o;
return model.equals(other.model)
&& signature.equals(other.signature)
&& height == other.height
&& width == other.width
&& transformations.equals(other.transformations)
&& resourceClass.equals(other.resourceClass)
&& transcodeClass.equals(other.transcodeClass)
&& options.equals(other.options);
}
return false;
}
@Override
public int hashCode() {
if (hashCode == 0) {
hashCode = model.hashCode();
hashCode = 31 * hashCode + signature.hashCode();
hashCode = 31 * hashCode + width;
hashCode = 31 * hashCode + height;
hashCode = 31 * hashCode + transformations.hashCode();
hashCode = 31 * hashCode + resourceClass.hashCode();
hashCode = 31 * hashCode + transcodeClass.hashCode();
hashCode = 31 * hashCode + options.hashCode();
}
return hashCode;
}
重写了equals和hashCode方法,以此保证EngineKey的唯一性,只有在传入的参数都相同的情况下才被认为是同一个对象
2. 内存缓存
根据文章开头的图片,这一层缓存用到了LruCache和WeakReference两种缓存机制,正在被展示的图片称为ActiveResourses,采用的就是WeakReference这种缓存机制,因此,我们先从这里开始
2.1 WeakReference缓存
回到Engine类的load方法,如果忘了可以往上翻翻,在其同步块里有一个loadFromMemory方法
Engine.java的loadFromMemory
@Nullable
private EngineResource loadFromMemory(
EngineKey key, boolean isMemoryCacheable, long startTime) {
if (!isMemoryCacheable) {
return null;
}
EngineResource active = loadFromActiveResources(key);
if (active != null) {
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return active;
}
EngineResource cached = loadFromCache(key);
if (cached != null) {
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return cached;
}
return null;
}
首先根据宽高,图片URL地址等生成key,然后根据key首先调用loadFromActiveResources获取内存的弱引用缓存的图片,如果获取不到弱引用缓存的图片,才调用loadFromCache获取内存的LruCache缓存。先来看看loadFromActiveResources
Engine.java的loadFromActiveResources
@Nullable
private EngineResource loadFromActiveResources(Key key) {
EngineResource active = activeResources.get(key);
if (active != null) {
active.acquire();
}
return active;
}
这里首先会调用ActiveResources的get方法获取到图片资源,然后将EngineResource的引用计数加一,因为此时EngineResource指向了弱引用缓存的图片资源
ActiveResources.java的get
final Map activeEngineResources = new HashMap<>();
@Nullable
synchronized EngineResource get(Key key) {
//从弱引用HashMap中取出对应的弱引用对象
ResourceWeakReference activeRef = activeEngineResources.get(key);
if (activeRef == null) {
return null;
}
//如果弱引用中关联的EngineResource对象不存在,即EngineResourse被回收
EngineResource active = activeRef.get();
if (active == null) {
//清理弱引用缓存,恢复EngineResource,并保存到LruCache缓存中
cleanupActiveReference(activeRef);
}
return active;
}
在ActiveResources中的get中,会通过activeEngineResources的get方法得到了数据的弱引用对象,而这个activeEngineResources其实就是个HashMap,所以可以根据key值来找到这个弱引用对象。而我们要找的图片资源其实就是这个弱引用对象关联的对象,因此我们需要查看ResourceWeakReference的实现
ResourceWeakReference.java
static final class ResourceWeakReference extends WeakReference> {
@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> queue,
boolean isActiveResourceRetentionAllowed) {
super(referent, queue);
this.key = Preconditions.checkNotNull(key);
this.resource =
referent.isMemoryCacheable() && isActiveResourceRetentionAllowed
? Preconditions.checkNotNull(referent.getResource())
: null;
isCacheable = referent.isMemoryCacheable();
}
···
}
}
可以发现这个类其实继承了WeakReference,所以当gc发生时,会回收掉ResourceWeakReference对象关联的EngineResource对象,这个对象封装了我们所要获取的图片资源。另外这个类还保存了图片资源和图片资源的缓存key,这是为了当关联的EngineResource对象被回收时,可以利用保存的图片资源来恢复EngineResource对象,然后保存到LruCache缓存中并根据key值从HashMap中删除掉关联了被回收的EngineResource对象的弱引用对象。可以看下EngineResourse被回收的情况,即上面get方法里的cleanupActiveReference方法
ActiveResources.java的cleanupActiveReference
void cleanupActiveReference(@NonNull ResourceWeakReference ref) {
synchronized (this) {
//将该弱引用缓存从HashMap中删除
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);
}
开头删除弱引用的缓存,在if语句中判断缓存是否可用。使用EngineResource恢复封装了图片资源的对象,调用listener的回调监听缓存是否被释放
Engine.java的onResourceReleased
public void onResourceReleased(Key cacheKey, EngineResource resource) {
//删除弱引用缓存
activeResources.deactivate(cacheKey);
if (resource.isMemoryCacheable()) {
cache.put(cacheKey, resource);
} else {
resourceRecycler.recycle(resource);
}
}
开头删除弱引用缓存,判断是否开启内存缓存,如果开启了就放入LruCache中,没开启就回收空间
2.2 LruCache缓存
上文已经看过代码,获取不到弱引用缓存时,才会获取LruCache缓存,对应调用的方法是loadFromCache
Engine.java的loadFromCache
private EngineResource loadFromCache(Key key) {
//获取图片缓存,并将该缓存从LruCache缓存中删除
EngineResource cached = getEngineResourceFromCache(key);
if (cached != null) {
//引用计数,防止被算法回收
cached.acquire();
activeResources.activate(key, cached);
}
return cached;
}
首先调用了getEngineResourceFromCache来获取图片资源,然后将EngineResource的引用计数加1,并且还会将获取到的图片资源存储到弱引用缓存中。这里我们只分析getEngineResourceFromCache方法,因为调用ActiveResource的activate存储到弱引用缓存我们已经在上面弱引用缓存的存储中分析过了
Engine.java的getEngineResourceFromCache
private EngineResource getEngineResourceFromCache(Key key) {
//获取到引用时,移出LruCache缓存
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;
}
在上面需注意的是该cache就是LruCache缓存的cache,另外你会发现获取图片缓存竟然不是调用cache的get方法,而是cache的remove方法,这就是Glide缓存策略的精妙之处了。当获取到LruCache缓存的同时会删除掉该LruCache缓存,然后将该缓存存储到弱引用缓存中,这是为了保护这些图片不会被LruCache算法回收掉
2.3 小结
3. 磁盘缓存
目的是为了防止重复从网络请求数据,采用Glide自定义的DiskLruCache缓存机制,使用代码如下
Glide.with(getContext())
.load(url)
.apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.RESOURCE))
.into(imageView);
通过diskCacheStrategyOf方法选择,有几种模式
DiskCacheStrategy.AUTOMATIC:这是默认的最优缓存策略。本地仅存储转换后的图片(RESOURCE),网络仅存储原始图片(DATA)
DiskCacheStrategy.NONE:不开启磁盘缓存
DiskCacheStrategy.RESOURCE:缓存转换过后的图片
DiskCacheStrategy.DATA:缓存原始图片,即原始输入流。它需要经过压缩转换,解析等操作才能最终展示出来
DiskCacheStrategy.ALL:既缓存原始图片,又缓存转换后的图片
所以其实观察一下可以发现主要就是RESOURCE类型和DATA类型,所以先回到Engine的load方法里
Engine.java的load
···
EngineJob engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
DecodeJob decodeJob =
decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);
jobs.put(key, engineJob);
engineJob.addCallback(cb, callbackExecutor);
engineJob.start(decodeJob);
···
return new LoadStatus(cb, engineJob);
}
这里会创建EngineJob对象,用于开启线程,所以必然会执行decodeJob的run方法
DecodeJob.java的run
public void run() {
···
runWrapped();
}
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
stage = getNextStage(Stage.INITIALIZE);
currentGenerator = getNextGenerator();
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
case DECODE_DATA:
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
在runWrapper方法的switch语句中,会获取任务场景NextStage,并拿到执行者执行任务,执行者有哪些呢?我们点进去看
DecodeJob.java的getNextGenerator
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);
}
}
可以发现在上述的方法中首先要找到对于场景的执行者然后执行任务。而执行者有三个,在上文第2部分中,我们分析的是无缓存的情况,即网络获取数据的执行者。接下来我们就得分析获取转换后图片的执行者(RESOURCE)和获取原始图片的执行者(DATA)
3.1 RESOURCE缓存,转换图片
对应于上面的switch语句,RESOURCE对应了ResourceCacheGenerator,拿到执行者后会回到runWrapper方法里进行runGenerators方法
DecodeJob.java的runGenerators
private void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
while (!isCancelled
&& currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
// We've run out of stages and generators, give up.
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}
// Otherwise a generator started a new load and we expect to be called back in
// onDataFetcherReady.
}
在while循环里,根据当前的执行者调用对应类的startNext()方法
ResourceCacheGenerator.java的startNext
public boolean startNext() {
···
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);
currentKey =
new ResourceCacheKey( // NOPMD AvoidInstantiatingObjectsInLoops
helper.getArrayPool(),
sourceId,
helper.getSignature(),
helper.getWidth(),
helper.getHeight(),
transformation,
resourceClass,
helper.getOptions());
cacheFile = helper.getDiskCache().get(currentKey);
if (cacheFile != null) {
sourceKey = sourceId;
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
ModelLoader 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;
}
首先根据图片的参数,宽高等信息拿到缓存key,然后通过缓存key获取到磁盘上的文件,即磁盘缓存。最后通过加载器的loadData来处理获取到的磁盘缓存,由于磁盘缓存是File类型,根据Glide的注册表registry中可以找到该加载器其实是ByteBufferFileLoader,故最后会调用ByteBufferFileLoader内部类ByteBufferFetcher的loadData方法来处理磁盘缓存
ByteBufferFetcher.java的loadData
@Override
public void loadData(
@NonNull Priority priority, @NonNull DataCallback callback) {
ByteBuffer result;
try {
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);
}
在loadData中会通过ByteBufferUtil工具类来获取对应磁盘文件的数据,然后通过回调ResourceCacheGenerator的onDataReady方法将数据回调出去
ResourceCacheGenerator.java的onDataReady
@Override
public void onDataReady(Object data) {
cb.onDataFetcherReady(
sourceKey, data, loadData.fetcher, DataSource.RESOURCE_DISK_CACHE, currentKey);
}
此时的cb为DecodeJob,即调用了DecodeJob的onDataFetcherReady
DecodeJob.java的onDataFetcherReady
public void onDataFetcherReady(
Key sourceKey, Object data, DataFetcher fetcher, DataSource dataSource, Key attemptedKey) {
this.currentSourceKey = sourceKey;
this.currentData = data;
this.currentFetcher = fetcher;
this.currentDataSource = dataSource;
this.currentAttemptingKey = attemptedKey;
if (Thread.currentThread() != currentThread) {
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
try {
decodeFromRetrievedData();
} finally {
GlideTrace.endSection();
}
}
}
decodeFromRetrievedData方法就是将数据进行压缩转换和展示图片
DecodeJob.java的decodeFromRetrievedData
private void decodeFromRetrievedData() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey(
"Retrieved data",
startFetchTime,
"data: "
+ currentData
+ ", cache key: "
+ currentSourceKey
+ ", fetcher: "
+ currentFetcher);
}
Resource resource = null;
try {
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
private void notifyEncodeAndRelease(Resource resource, DataSource dataSource) {
if (resource instanceof Initializable) {
((Initializable) resource).initialize();
}
Resource result = resource;
LockedResource lockedResource = null;
if (deferredEncodeManager.hasResourceToEncode()) {
lockedResource = LockedResource.obtain(resource);
result = lockedResource;
}
notifyComplete(result, dataSource);
stage = Stage.ENCODE;
try {
if (deferredEncodeManager.hasResourceToEncode()) {
deferredEncodeManager.encode(diskCacheProvider, options);
}
} finally {
if (lockedResource != null) {
lockedResource.unlock();
}
}
// Call onEncodeComplete outside the finally block so that it's not called if the encode process
// throws.
onEncodeComplete();
}
如果resource不为null,就会调用notifyEncodeAndRelease,在try语句中的encode方法就是RESOURCE类型磁盘缓存的入口
DecodeJob.java的encode
void encode(DiskCacheProvider diskCacheProvider, Options options) {
GlideTrace.beginSection("DecodeJob.encode");
try {
diskCacheProvider
.getDiskCache()
.put(key, new DataCacheWriter<>(encoder, toEncode, options));
} finally {
toEncode.unlock();
GlideTrace.endSection();
}
}
通过DiskCacheProvider获取到DiskCache,然后调用put方法将图片资源缓存到磁盘上
3.2 DATA缓存,原始图片
我们上面已经知道DATA和RESOURCE只是两种不同类型的执行者,
调用的方法是一致的,只不过参数不同,所以这里不再重复。而原始图片说白了就是网络获取后得到的原始的输入流,获取到原始输入流是在HttpUrlFetcher的loadData方法中,这个类实现了DataFetcher接口
HttpUrlFetcher.java的loadData
@Override
public void loadData(
@NonNull Priority priority, @NonNull DataCallback callback) {
long startTime = LogTime.getLogTime();
try {
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
callback.onDataReady(result);
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to load data for url", e);
}
callback.onLoadFailed(e);
} finally {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime));
}
}
}
通过输入流创建result,即网络获取的数据,然后通过回调转到onDataReady
SourceGenerator.java的onDataReady
@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);
}
}
如果开启了磁盘缓存,就将网络获取到的原始数据赋值给dataToCache,回调了DecodeJob的reschedule
DecodeJob.java的reschedule
@Override
public void reschedule() {
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
callback.reschedule(this);
}
继续回调了reschedule
public void reschedule(DecodeJob job) {
//再次切换到网络线程,执行DecodeJob的run方法
getActiveSourceExecutor().execute(job);
}
这里的job为DecodeJob,而getActiveSourceExecutor会拿到线程池,所以reschedule方法其实会继续执行DecodeJob的run方法,然后拿到网络获取数据的执行者SourceGenerator,再次执行SourceGenerator的startNext方法。但是我们明明开启了磁盘缓存,为什么会获取到无缓存时网络获取数据的执行者呢?这是因为我们在存储原始图片的前提下,肯定是磁盘没有缓存,因此会从网络加载图片得到原始图片的输入流,然后回调,回调后当然还是拿到网络获取数据的执行者SourceGenerator。让我们再来看看这个startNext方法
SourceGenerator.java的startNext
@Override
public boolean startNext() {
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
//当原始图片放入磁盘缓存后,sourceCacheGenerator为DataCacheGenerator
//然后继续执行DataCacheGenerator的startNext方法
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;
}
SOURCE执行者的startNext略有不同,dataCache不为null时放入缓存cacheData
SourceGenerator.java的cacheData
private void cacheData(Object dataToCache) {
long startTime = LogTime.getLogTime();
try {
Encoder