介绍
开始讲之前弄清楚几个概念和作用:
- Model:资源地址 url,uri, file,string等等
- Data:数据从Model加载得到的数据InputStream,ByteBuffer等等
- ModelLoader<Model, Data>:Model加载器
- Resource:资源 Bitmap,BitmapDrawable等等
- ResourceTranscoder:资源转码器 比如将bitmap转为bitmapDrawable
通过第一篇文章中的那张时序图,我们知道真正的加载资源的实现是在RequestTracker中的runRequest方法中。RequestTracker#runRequest方法
public void runRequest(@NonNull Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
request.clear();
...
pendingRequests.add(request);
}
}
主流程应该是走request.begin();其中request是接口类,这里的实现类是SingleRequest类
public void begin() {
synchronized (requestLock) {
....
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);//1
}
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());//2
}
...
}
}
我们没有设置宽度和高度,就执行注释1,通过监听在系统绘制view之前得到view的宽高尺寸,我们直接看回调结果onSizeReady方法,下边的注释2地方是设置占位图。
public void onSizeReady(int width, int height) {
...
synchronized (requestLock) {
...
status = Status.RUNNING;
....
loadStatus =
engine.load(
glideContext,
model,
requestOptions.getSignature(),
this.width,
this.height,
requestOptions.getResourceClass(),
transcodeClass,
priority,
requestOptions.getDiskCacheStrategy(),
requestOptions.getTransformations(),
requestOptions.isTransformationRequired(),
requestOptions.isScaleOnlyOrNoTransform(),
requestOptions.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getUseAnimationPool(),
requestOptions.getOnlyRetrieveFromCache(),
this,
callbackExecutor);
...
}
}
这里看到了真正处理请求的地方是在engine.load()函数中。
Engine#load
public <R> LoadStatus load(
...) {
...
EngineResource<?> memoryResource;
synchronized (this) {
//1.先从内存中加载资源
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
//2.等待已存在的任务或者开启新任务
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);
}
}
// 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;
}
/**
* 从内存中加载资源
* @param key
* @param isMemoryCacheable
* @param startTime
* @return
*/
@Nullable
private EngineResource<?> loadFromMemory(
EngineKey key, boolean isMemoryCacheable, long startTime) {
if (!isMemoryCacheable) {//设置禁止使用缓存
return null;
}
//1.从活动资源中根据key加载资源
EngineResource<?> active = loadFromActiveResources(key);
if (active != null) {
...
return active;
}
//2.从缓存中根据key 加载资源
EngineResource<?> cached = loadFromCache(key);
if (cached != null) {
......
return cached;
}
return null;
}
从缓存中获取资源的时候,第一步是从活动资源中根据key加载资源
private EngineResource<?> loadFromActiveResources(Key key) {
EngineResource<?> active = activeResources.get(key);
if (active != null) {
//活动资源中存在 引用计数+1,使用活动资源
active.acquire();
}
return active;
}
活动资源中没有找到资源,就从缓存中获取
private EngineResource<?> loadFromCache(Key key) {
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
//缓存中有资源就放入活动资源中并且引用计数+1
cached.acquire();
activeResources.activate(key, cached);
}
return cached;
}
private EngineResource<?> getEngineResourceFromCache(Key key) {
Resource<?> cached = cache.remove(key);//1
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;
}
注意getEngineResourceFromCache 中是从缓存移除的资源,loadFromCache中从缓存中得到资源引用计数+1,并且放入活动资源中。回到上边的load方法内,如果活动资源和缓存中都没有的话,一般就是我们第一次加载资源的时候或者设置禁用缓存,就执行waitForExistingOrStartNewJob方法,这个方法名字望文生义也应该知道是等待同样的任务,或者启动新任务。
private <R> LoadStatus waitForExistingOrStartNewJob(
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,
EngineKey key,
long startTime) {
/**之前有相同的加载任务,就使用上一次的加载任务,并把回调放入上次的任务中**/
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb, callbackExecutor);
return new LoadStatus(cb, current);
}
EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
DecodeJob<R> decodeJob =
decodeJobFactory.build(
...);
jobs.put(key, engineJob);
engineJob.addCallback(cb, callbackExecutor);
engineJob.start(decodeJob);
return new LoadStatus(cb, engineJob);
}
EngineJob#start方法
public synchronized void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor =
decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
executor.execute(decodeJob);
}
解码资源的来源是磁盘缓存就使用GlideBuilder中配置的磁盘缓存线程池,否则使用活动资源的线程池,这里可以看出decodeJob是一个Runnable的实现类
DecodeJob#run方法
@Override
public void run() {
DataFetcher<?> localFetcher = currentFetcher;
try {
if (isCancelled) {
notifyFailed();
return;
}
runWrapped();
}....
}
重点是runWrapped方法,国外的工程师给方法取名的时候,名字很直白的体现了他的作用,这里一看就是执行包装过程,真正的加载model,解码data得到资源的过程都是在这个方法中。
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);
}
}
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);
}
}
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.
}
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);
}
}
根据执行的原因(runReason)得到下一个阶段标记(stage),再根据stage得到当前的生成器(currentGenerator),最后使用这个生成器startNext,执行加载,并且把结果回传到这个类中。整个执行过程就是一个责任链模式,任务从INITIALIZE阶段传到SOURCE阶段,直到有一个能处理任务的就结束。
这里的三个生成器ResourceCacheGenerator,DataCacheGenerator,SourceGenerator 的对象的第二个参数都是this,即FetcherReadyCallback接口的对象,生成器生成的数据(bytebuffer, inputstream等)都会回调到当前对象中的。
@Override
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();
}
}
}
/**从接收到的数据解码*/
private void decodeFromRetrievedData() {
...
Resource<R> 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();
}
}
/**使用DataFetcher 根据Data解码*/
private <Data> Resource<R> decodeFromData(
DataFetcher<?> fetcher, Data data, DataSource dataSource) throws GlideException {
try {
...
Resource<R> result = decodeFromFetcher(data, dataSource);
...
return result;
} finally {
fetcher.cleanup();
}
}
@SuppressWarnings("unchecked")
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
return runLoadPath(data, dataSource, path);
}
private <Data, ResourceType> Resource<R> runLoadPath(
Data data, DataSource dataSource, LoadPath<Data, ResourceType, R> path)
throws GlideException {
Options options = getOptionsWithHardwareConfig(dataSource);
DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
try {
// ResourceType in DecodeCallback below is required for compilation to work with gradle.
return path.load(
rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
} finally {
rewinder.cleanup();
}
}
剩下的解码工作都是在LoadPath#load方法中执行的,本篇文章暂不讨论解码流程。
DecodeJob中资源的加载流程
private void runWrapped() {
switch (runReason) {
case INITIALIZE://1.
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);
}
}
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);
}
}
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);
}
}
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.
}
init方法中初始化runReason = INITIALIZE;第一步getNextStage 得到的是Stage.RESOURCE_CACHE,资源缓存阶段,getNextGenerator() 方法得到的是ResourceCacheGenerator 对象,ResourceCacheGenerator 的startNext()方法负责根据key和资源类型从注册机中找出MoldelLoader,然后根据Data类型,资源类型(bitmap),转码类型(drawable),ResourceCacheGenerator#startNext方法
public boolean startNext() {
List<Key> sourceIds = helper.getCacheKeys();
if (sourceIds.isEmpty()) {
return false;
}
List<Class<?>> resourceClasses = helper.getRegisteredResourceClasses();
...
//检索出合适的ModelLoader
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<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
//数据加载类 model-data url-inputstream
loadData =
modelLoader.buildLoadData(
cacheFile, helper.getWidth(), helper.getHeight(), helper.getOptions());
//能够解码(data-resource inputstream-) 并且转码(resource-transcode bitmap-BitmapDrawable)
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
资源缓存生成器(ResourceCacheGenerator)主要是从磁盘目录中找出和目标资源尺寸大小,转换(fitCenter等)后的精准缓存资源(bitmap)。如果找不到就执行下一步DATA_CACHE,DataCacheGenerator#startNext()
@Override
public boolean startNext() {
while (modelLoaders == null || !hasNextModelLoader()) {
sourceIdIndex++;
if (sourceIdIndex >= cacheKeys.size()) {
return false;
}
Key sourceId = cacheKeys.get(sourceIdIndex);
// PMD.AvoidInstantiatingObjectsInLoops The loop iterates a limited number of times
// and the actions it performs are much more expensive than a single allocation.
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
cacheFile = helper.getDiskCache().get(originalKey);
if (cacheFile != null) {
this.sourceKey = sourceId;
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}
loadData = null;
boolean started = false;
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;
}
DataCacheGenerator 从磁盘中查询未经修改的原始数据。如果磁盘中原始数据也没有的情况,就执行SOURCE阶段,runGenerators()方法中的SOURCE阶段执行,currentGenerator = new SourceGenerator(decodeHelper, this);和 reschedule();
@Override
public void reschedule() {
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
callback.reschedule(this);
}
这里重置runReason = RunReason.SWITCH_TO_SOURCE_SERVICE; callback的实现类是EngineJob
EngineJob#reschedule(DecodeJob<?> job)
@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);
}
切换到活动资源的线程加载资源,我们还回到DecodeJob中runWrapped()方法执行的是SWITCH_TO_SOURCE_SERVICE,runGenerators() 中执行SourceGenerator的startNext()
SourceGenerator# startNext()
@Override
public boolean startNext() {
if (dataToCache != null) {//1
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {//2
return true;
}
sourceCacheGenerator = null;
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {//3
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);//4
}
}
return started;
}
第一次进来执行注释3处,根据model查找LoadData,执行注释4的代码。model的加载结果会回调给onDataReady方法
Override
public void onDataReady(Object data) {
//
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {//1
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();//2
} else {
cb.onDataFetcherReady(
loadData.sourceKey,
data,
loadData.fetcher,
loadData.fetcher.getDataSource(),
originalKey);
}
}
默认的磁盘缓存策略是AUTOMATIC,是支持数据缓存的。所以执行注释1逻辑,会将加载的data传递给全局变量dataToCache ,注释2处又执行DecodeJob中的reschedule()方法,即又切换一次线程,当前的currentGenerator还是SourceGenerator对象,所以又执行一次startNext()方法,不过这一次的dataToCache不是null了,所以执行cacheData(data);
/**
* 将解码数据写入到磁盘缓存中
* @param dataToCache 要缓存的数据
*/
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);//写入磁盘内
...
} finally {
loadData.fetcher.cleanup();
}
//磁盘缓存生成器
sourceCacheGenerator =
new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
}
接着startNext()方法中会执行sourceCacheGenerator.startNext(),即又回到了DataCacheGenerator的startNext()中。
小结:SourceGenerator主要做了两件事:
- 1.加载之前切换线程加载model(资源地址-URL)生成Data(Inputstream);
- 2.切换磁盘线程,Data写入磁盘内,生成DataCacheGenerator,使用DataCacheGenerator加载数据。