上一节我们说到了如何获取磁盘缓存和开启图片请求的,需要回顾的同学可以继续查看:
HttpUrlFetcher loadData()
@Override
public void loadData(@NonNull Priority priority,
@NonNull DataCallback<? super InputStream> 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));
}
}
}
上一节我们讲述了HttpUrlFetcher调用loadData请求图片数据并且返回图片对应的InputStream,那么这一节我们着重看下是如何将InputStream回调到最初的ViewTarget并且显示的,所以这里我们要看仔细每一个回调监听。这里我们看到调用了callback.onDataReady(result)方法,那么callback是由loaddata传进来的,我们返回再看看是怎么入参的:
SourceGenerator
@Override
public boolean startNext() {
.........
.........
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;
}
可以看到SourceGenerator传入的是this,所以上面的onDataReady方法回调到SourceGenerator的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);
}
}
这里isDataCacheable是去判读是否能缓存到磁盘,如果不能,就继续往上回调,如果可以缓存,那么将当前的inputstream赋值给dataToCache,然后调用cb.reschedule(),我们来看看这个cb监听是什么时候传进来的:
private final FetcherReadyCallback cb;
private int loadDataListIndex;
private DataCacheGenerator sourceCacheGenerator;
private Object dataToCache;
private volatile ModelLoader.LoadData<?> loadData;
private DataCacheKey originalKey;
SourceGenerator(DecodeHelper<?> helper, FetcherReadyCallback cb) {
this.helper = helper;
this.cb = cb;
}
可以看到FetcherReadyCallback是在SourceGenerator初始化的时候传进来的,那么看下SourceGenerator的初始化方法:
DecodeJob
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);
}
}
可以看到是在DecodeJob的getNextGenerator方法中去初始化的,所以我们看看他的reschedule方法:
@Override
public void reschedule() {
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
callback.reschedule(this);
}
@Override
public void reschedule(DecodeJob<?> job) {
getActiveSourceExecutor().execute(job);
}
可以看到还是重新执行了DecodeJob线程,直接去加载SourceGenerator,我们跟进去看一下源码:
@Override
public boolean startNext() {
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
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 = 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();
}
sourceCacheGenerator =
new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
}
由于我们之前已经分析过这一块的startNext方法,所以这里调用的是SourceGenerator的startNext,可以看到第一行直接判读按dataToCache非空,dataToCache在刚才获取input stream的时候已经赋值给dataToCache,所以这里直接执行cacheData方法去缓存数据。cacheData里面的helper.getSourceEncoder是通过传入的data类型,返回一个Encoder对应的解码器。然后又生成了一个DataCacheWriter包装类,传入了解码器和dataToCache流数据,调用 helper.getDiskCache().put方法,我们跟进去看下put方法:
@Override
public void put(Key key, Writer writer) {
String safeKey = safeKeyGenerator.getSafeKey(key);
writeLocker.acquire(safeKey);
try {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Put: Obtained: " + safeKey + " for for Key: " + key);
}
try {
DiskLruCache diskCache = getDiskCache();
Value current = diskCache.get(safeKey);
if (current != null) {
return;
}
DiskLruCache.Editor editor = diskCache.edit(safeKey);
if (editor == null) {
throw new IllegalStateException("Had two simultaneous puts for: " + safeKey);
}
try {
File file = editor.getFile(0);
if (writer.write(file)) {
editor.commit();
}
} finally {
editor.abortUnlessCommitted();
}
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Unable to put to disk cache", e);
}
}
} finally {
writeLocker.release(safeKey);
}
}
可以看到传进来的writer调用了write(file),这里我们就不跟进去了,就是一般的读取流然后存储到文件,这里就把数据缓存到了硬盘缓存,回到哦最初的cacheData方法,重新去new DataCacheGenerator并且传入的监听是this,sourceCacheGenerator = new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this),然后继续调用startStage中的sourceCacheGenerator.startNext,则去调用了DataCacheGenerator的startNext方法,继续看:
@Override
public boolean startNext() {
while (modelLoaders == null || !hasNextModelLoader()) {
sourceIdIndex++;
if (sourceIdIndex >= cacheKeys.size()) {
return false;
}
Key sourceId = cacheKeys.get(sourceIdIndex);
@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;
}
这里为什么又去执行一次呢,其实这里是从磁盘缓存中去读取刚刚我们缓存的数据,那么这里的loadData.fetcher.loadData则去缓存中加载缓存图片文件并且再去回调,我们看下FileFetcher的loadData方法:
@Override
public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super Data> callback) {
try {
data = opener.open(file);
} catch (FileNotFoundException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to open file", e);
}
callback.onLoadFailed(e);
return;
}
callback.onDataReady(data);
}
直接通过opener.open去获取文件,加载的回调就在DataCacheGenerator的onDataReady中
@Override
public void onDataReady(Object data) {
cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);
}
@Override
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
DataSource dataSource, Key attemptedKey) {
cb.onDataFetcherReady(sourceKey, data, fetcher, loadData.fetcher.getDataSource(), sourceKey);
}
@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();
}
}
}
继续回调依然是到上层的sourceCacheGenerator的onDataFetcherReady中,然后sourceCacheGenerator回调到Decode中的onDataFetcherReady方法,这里的currentThread从开启子线程执行DecodeJobs后没有做过线程切换,所以依然是当前线程,所以执行callback.reschedule(this)方法并且赋值runReason为DECODE_DATA准备再次执行Decode线程去解析磁盘缓存回调的数据:
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 void decodeFromRetrievedData() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Retrieved data", startFetchTime,
"data: " + currentData
+ ", cache key: " + currentSourceKey
+ ", fetcher: " + currentFetcher);
}
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();
}
}
由于是 runReason = RunReason.DECODE_DATA,所以直接执行decodeFromRetrievedData,这里调用了decodeFromData去解析数据,并返回一个Resource,这里的Resource就是加载和解析过后的数据,不去细究,直接看回调notifyEncodeAndRelease方法:
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
if (resource instanceof Initializable) {
((Initializable) resource).initialize();
}
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()) {
deferredEncodeManager.encode(diskCacheProvider, options);
}
} finally {
if (lockedResource != null) {
lockedResource.unlock();
}
}
onEncodeComplete();
}
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
setNotifiedOrThrow();
callback.onResourceReady(resource, dataSource);
}
这里直接看回调notifyComplete方法,调用了callback.onResourceReady方法,可能我们已经忘了这里的callback到底是什么时候传进来的,来看一下:
DecodeJob<R> init(
GlideContext glideContext,
Object model,
EngineKey loadKey,
Key signature,
int width,
int height,
Class<?> resourceClass,
Class<R> transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map<Class<?>, Transformation<?>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
boolean onlyRetrieveFromCache,
Options options,
Callback<R> callback,
int order) {
......
this.callback = callback;
this.order = order;
this.runReason = RunReason.INITIALIZE;
this.model = model;
return this;
}
可以看到调用的是DecodeJob的initial方法,最终发现这里的callback其实就是EngineJob本身,所以看一看EngineJob的onResourceReady方法:
@Override
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
synchronized (this) {
this.resource = resource;
this.dataSource = dataSource;
}
notifyCallbacksOfResult();
}
这里分别赋值了resource和dataSource,这两个变量就是我们说到的磁盘缓存的两种类型,继续看notifyCallbacksOfResult方法:
void notifyCallbacksOfResult() {
ResourceCallbacksAndExecutors copy;
Key localKey;
EngineResource<?> localResource;
synchronized (this) {
stateVerifier.throwIfRecycled();
if (isCancelled) {
resource.recycle();
release();
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received a resource without any callbacks to notify");
} else if (hasResource) {
throw new IllegalStateException("Already have resource");
}
engineResource = engineResourceFactory.build(resource, isCacheable);
hasResource = true;
copy = cbs.copy();
incrementPendingCallbacks(copy.size() + 1);
localKey = key;
localResource = engineResource;
}
listener.onEngineJobComplete(this, localKey, localResource);
for (final ResourceCallbackAndExecutor entry : copy) {
entry.executor.execute(new CallResourceReady(entry.cb));
}
decrementPendingCallbacks();
}
@Override
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.activate(key, resource);
}
jobs.removeIfCurrent(key, engineJob);
}
synchronized void activate(Key key, EngineResource<?> resource) {
ResourceWeakReference toPut =
new ResourceWeakReference(
key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);
ResourceWeakReference removed = activeEngineResources.put(key, toPut);
if (removed != null) {
removed.reset();
}
}
ResourceCallbacksAndExecutors 这里存储的是回调的线程池执行器,我们先看后面,有一个EngineResource对象,还记得之前我们的内存缓存吗,对了,这里就是新建一个缓存对象,下面的engineResourceFactory.build方法,就是通过传入已经加载完成的resource生成EngineResource内存缓存。继续看listener的onEngineJobComplete方法,传入了key和engineResource,这里我们看到了两个熟悉的变量,isMemoryCacheable和activeResources,就是判断是否缓存到内存,如果是,那么直接通过activeResources的activate方法构造出ResourceWeakReference并且存储到弱引用缓存。
回到上面的notifyCallbacksOfResult,存储到内存缓存后,应该开始回调到最初的加载的地方并且设置图片了,我们继续看下entry.executor.execute(new CallResourceReady(entry.cb))方法,首先entry里存储的就是copy的ResourceCallbackAndExecutor,而copy是通过调用cbs的copy方法获取的,我们跟进去看一下
static final class ResourceCallbacksAndExecutors
implements Iterable<ResourceCallbackAndExecutor> {
private final List<ResourceCallbackAndExecutor> callbacksAndExecutors;
ResourceCallbacksAndExecutors() {
this(new ArrayList<ResourceCallbackAndExecutor>(2));
}
void add(ResourceCallback cb, Executor executor) {
callbacksAndExecutors.add(new ResourceCallbackAndExecutor(cb, executor));
}
void remove(ResourceCallback cb) {
callbacksAndExecutors.remove(defaultCallbackAndExecutor(cb));
}
ResourceCallbacksAndExecutors copy() {
return new ResourceCallbacksAndExecutors(new ArrayList<>(callbacksAndExecutors));
}
发现callbacksAndExecutors就是ResourceCallbacksAndExecutors的一个变量,通过调用add方法添加Executor,那么我们就跟进去看在什么时候添加了这个回调执行器。
synchronized void addCallback(final ResourceCallback cb, Executor callbackExecutor) {
stateVerifier.throwIfRecycled();
cbs.add(cb, callbackExecutor);
if (hasResource) {
incrementPendingCallbacks(1);
callbackExecutor.execute(new CallResourceReady(cb));
} else if (hasLoadFailed) {
incrementPendingCallbacks(1);
callbackExecutor.execute(new CallLoadFailed(cb));
} else {
Preconditions.checkArgument(!isCancelled, "Cannot add callbacks to a cancelled EngineJob");
}
}
jobs.put(key, engineJob);
engineJob.addCallback(cb, callbackExecutor);
engineJob.start(decodeJob);
我们发现,callbackExecutor回调执行器在请求图片资源,开启decodeJob的时候就添加进来了,继续往上跟,看清楚callbackExecutor的真正对象是什么:
<Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
Executor callbackExecutor) {
return into(target, targetListener, /*options=*/ this, callbackExecutor);
}
public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) {
return into(target, /*targetListener=*/ null, Executors.mainThreadExecutor());
}
private static final Executor MAIN_THREAD_EXECUTOR =
new Executor() {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override
public void execute(@NonNull Runnable command) {
handler.post(command);
}
};
跟到最初始的地方我们发现,原来在调用into方法的时候就已经传入了一个主线程的执行器,所以回到我们存储内存缓存即将回调的地方:
for (final ResourceCallbackAndExecutor entry : copy) {
entry.executor.execute(new CallResourceReady(entry.cb));
}
public void run() {
synchronized (cb.getLock()) {
synchronized (EngineJob.this) {
if (cbs.contains(cb)) {
engineResource.acquire();
callCallbackOnResourceReady(cb);
removeCallback(cb);
}
decrementPendingCallbacks();
}
}
}
void callCallbackOnResourceReady(ResourceCallback cb) {
try {
cb.onResourceReady(engineResource, dataSource);
} catch (Throwable t) {
throw new CallbackException(t);
}
}
所以这里的executor,必定存在主线程的MAIN_THREAD_EXECUTOR,所以在这里实现了线程切换,将数据回调到了主线程,我们直接看CallResourceReady的run方法,engineResource.acquire()开启了引用计数,表示当前内存缓存已经引用了,继续调用callCallbackOnResourceReady方法,这里的cb.onResourceReady方法就回调到了singleRequest中,我们跟进去看:
public void onResourceReady(Resource<?> resource, DataSource dataSource) {
stateVerifier.throwIfRecycled();
Resource<?> toRelease = null;
try {
synchronized (requestLock) {
loadStatus = null;
......
......
if (!canSetResource()) {
toRelease = resource;
this.resource = null;
status = Status.COMPLETE;
return;
}
onResourceReady((Resource<R>) resource, (R) received, dataSource);
}
} finally {
if (toRelease != null) {
engine.release(toRelease);
}
}
}
private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
......
isCallingCallbacks = true;
try {
boolean anyListenerHandledUpdatingTarget = false;
if (requestListeners != null) {
for (RequestListener<R> listener : requestListeners) {
anyListenerHandledUpdatingTarget |=
listener.onResourceReady(result, model, target, dataSource, isFirstResource);
}
}
anyListenerHandledUpdatingTarget |=
targetListener != null
&& targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
if (!anyListenerHandledUpdatingTarget) {
Transition<? super R> animation = animationFactory.build(dataSource, isFirstResource);
target.onResourceReady(result, animation);
}
} finally {
isCallingCallbacks = false;
}
notifyLoadSuccess();
}
onResourceReady省略的地方做了一些判断和失败的回调,可以忽略,我们直接看下面的onResourceReady方法,requestListeners和targetListener是最初我们自己是否添加过加载监听,如果设置过监听,这里就会回调,我们没有设置直接看target.onResourceReady方法,还记得我们的imageview设置到哪里了么,就是target中,因此这里就将数据源回调到了image view中,因此这里调用imageviewtarget的onResourceReady方法:
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
if (transition == null || !transition.transition(resource, this)) {
setResourceInternal(resource);
} else {
maybeUpdateAnimatable(resource);
}
}
private void setResourceInternal(@Nullable Z resource) {
// non-null Callback before starting it.
setResource(resource);
maybeUpdateAnimatable(resource);
}
DrawableImageViewTarget
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
这里的setResource方法也就调用了DrawableImageViewTarget的setResource方法,可以看到又调用了view的setImageDrawable方法,将图片设置到view上了,到这里从加载数据结束到存储到缓存并加载到view上的流程就分析结束了。
总结
HttpUrlFetcher 请求玩图片后返回给SourceGenerator图片的input stream,然后重新执行DecodeJob,调用SourceGenerator的startNext的cacheData方法去缓存数据到磁盘然后调用DataCacheGenerator的startNext方法又去磁盘缓存中加载数据,加载完成后再次执行DecodeJob方法,去decode解析磁盘缓存得到resource对象,完成后回调到EngineJob中,然后将resource存储到弱引用内存缓存中,引用计数加一,调用主线程的handler将数据回调到主线程的Target中加载并显示图片。执行流程就是SourceGenerator-->DataCacheGenerator-->DecodeJob-->EngineJob-->SingleRequest-->Target。到这里整个图片的加载流程就分析结束了,下一节我们再讲讲面试中Glide源码可能关注的一些问题~