本篇是 Glide 系列的最后一篇,主要講一下 into 方法里面的邏輯。into 的邏輯也是最多最復(fù)雜的鬓梅,可能需要反復(fù)閱讀源碼才能搞清楚。
Glide : https://github.com/bumptech/glide
version : v4.9.0
RequestBuilder
@NonNull
public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) {
return into(target, /*targetListener=*/ null, Executors.mainThreadExecutor());
}
@NonNull
@Synthetic
<Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
Executor callbackExecutor) {
return into(target, targetListener, /*options=*/ this, callbackExecutor);
}
into 方法最后都會(huì)調(diào)用 into(@NonNull Y target, @Nullable RequestListener<TranscodeType> targetListener, BaseRequestOptions<?> options, Executor callbackExecutor)
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
Preconditions.checkNotNull(target);
// 判斷有沒(méi)有調(diào)用過(guò) load 方法
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
// 這里根據(jù)一堆參數(shù)會(huì)去構(gòu)造圖片請(qǐng)求 SingleRequest
Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
request.recycle();
// If the request is completed, beginning again will ensure the result is re-delivered,
// triggering RequestListeners and Targets. If the request is failed, beginning again will
// restart the request, giving it another chance to complete. If the request is already
// running, we can let it continue running without interruption.
if (!Preconditions.checkNotNull(previous).isRunning()) {
// Use the previous request rather than the new one to allow for optimizations like skipping
// setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions
// that are done in the individual Request.
previous.begin();
}
return target;
}
requestManager.clear(target);
target.setRequest(request);
requestManager.track(target, request);
return target;
}
構(gòu)造出請(qǐng)求 request 后谨湘,重點(diǎn)關(guān)注下 requestManager.track 方法绽快,由 requestManager 來(lái)執(zhí)行這個(gè)請(qǐng)求。
RequestManager
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
track 方法中調(diào)用了兩個(gè)方法:
- TargetTracker.track() 方法會(huì)對(duì)當(dāng)前 Target 的生命周期進(jìn)行管理;
- RequestTracker.runRequest() 方法對(duì)當(dāng)前請(qǐng)求進(jìn)行管理;
RequestTracker
public void runRequest(@NonNull Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
request.clear();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Paused, delaying request");
}
pendingRequests.add(request);
}
}
當(dāng) Glide 未處于暫停狀態(tài)的時(shí)候紧阔,會(huì)直接使用 Request.begin() 方法開(kāi)啟請(qǐng)求坊罢。
SingleRequest
@Override
public synchronized void begin() {
assertNotCallingCallbacks();
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
if (model == null) {
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
width = overrideWidth;
height = overrideHeight;
}
// Only log at more verbose log levels if the user has set a fallback drawable, because
// fallback Drawables indicate the user expects null models occasionally.
int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
onLoadFailed(new GlideException("Received null model"), logLevel);
return;
}
if (status == Status.RUNNING) {
throw new IllegalArgumentException("Cannot restart a running request");
}
// If we're restarted after we're complete (usually via something like a notifyDataSetChanged
// that starts an identical request into the same Target or View), we can simply use the
// resource and size we retrieved the last time around and skip obtaining a new size, starting a
// new load etc. This does mean that users who want to restart a load because they expect that
// the view size has changed will need to explicitly clear the View or Target before starting
// the new load.
if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
// Restarts for requests that are neither complete nor running can be treated as new requests
// and can run again from the beginning.
// 主要來(lái)看這里,之前的都是一些對(duì)請(qǐng)求的狀態(tài)判斷
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
在 begin 方法中擅耽,重點(diǎn)關(guān)注下
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
這里會(huì)根據(jù)有沒(méi)有強(qiáng)制設(shè)置圖片寬度來(lái)分為兩部分:
- 設(shè)置了活孩,直接調(diào)用 onSizeReady
- 沒(méi)設(shè)置,會(huì)去調(diào)用 target.getSize 乖仇。做的事情就是給 view 添加 addOnPreDrawListener 憾儒。這樣的話,在繪制之前獲取到了 view 的寬高这敬,然后再回調(diào) onSizeReady
所以航夺,說(shuō)到底,最后都是會(huì)調(diào)用 onSizeReady 的崔涂。
@Override
public synchronized void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
if (IS_VERBOSE_LOGGABLE) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
if (IS_VERBOSE_LOGGABLE) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
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);
// This is a hack that's only useful for testing right now where loads complete synchronously
// even though under any executor running on any thread but the main thread, the load would
// have completed asynchronously.
if (status != Status.RUNNING) {
loadStatus = null;
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}
在 onSizeReady 中將狀態(tài)更改為 Status.RUNNING 阳掐,并調(diào)用 engine 的 load() 方法。
Engine
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) {
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
// 構(gòu)造此圖片的緩存 key
EngineKey key =
keyFactory.buildKey(
model,
signature,
width,
height,
transformations,
resourceClass,
transcodeClass,
options);
EngineResource<?> memoryResource;
synchronized (this) {
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
// 查看有沒(méi)有圖片緩存冷蚂,如果沒(méi)有的話就開(kāi)啟新的任務(wù)加載
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.
// 如果有的話缭保,直接回調(diào) SingleRequest 的 onResourceReady 方法
cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
return null;
}
在 load 中,Glide 會(huì)先去取緩存蝙茶,當(dāng)緩存中不存在的時(shí)候就準(zhǔn)備使用新創(chuàng)建一個(gè)任務(wù)來(lái)加載圖片艺骂。我們這里就當(dāng)作是第一次加載圖片了,所以跟進(jìn) 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);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
DecodeJob<R> 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);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
上面方法中創(chuàng)建了兩個(gè)對(duì)象钳恕,一個(gè)是 DecodeJob别伏、一個(gè)是 EngineJob。它們之間的關(guān)系是忧额,EngineJob 內(nèi)部維護(hù)了線程池厘肮,用來(lái)管理資源加載,已經(jīng)當(dāng)資源加載完畢的時(shí)候通知回調(diào)睦番。 DecodeJob 繼承了 Runnable类茂,是線程池當(dāng)中的一個(gè)任務(wù)。就像上面那樣托嚣,我們通過(guò)調(diào)用 engineJob.start(decodeJob) 來(lái)開(kāi)始執(zhí)行圖片加載的任務(wù)巩检。
EngineJob
public synchronized void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor =
decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
executor.execute(decodeJob);
}
執(zhí)行 decodeJob 。所以直接看 DecodeJob 的 run 方法示启。
DecodeJob
@Override
public void run() {
// This should be much more fine grained, but since Java's thread pool implementation silently
// swallows all otherwise fatal exceptions, this will at least make it obvious to developers
// that something is failing.
GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);
// Methods in the try statement can invalidate currentFetcher, so set a local variable here to
// ensure that the fetcher is cleaned up either way.
DataFetcher<?> localFetcher = currentFetcher;
try {
if (isCancelled) {
notifyFailed();
return;
}
runWrapped();
} catch (CallbackException e) {
// If a callback not controlled by Glide throws an exception, we should avoid the Glide
// specific debug logic below.
throw e;
} catch (Throwable t) {
// Catch Throwable and not Exception to handle OOMs. Throwables are swallowed by our
// usage of .submit() in GlideExecutor so we're not silently hiding crashes by doing this. We
// are however ensuring that our callbacks are always notified when a load fails. Without this
// notification, uncaught throwables never notify the corresponding callbacks, which can cause
// loads to silently hang forever, a case that's especially bad for users using Futures on
// background threads.
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(
TAG,
"DecodeJob threw unexpectedly" + ", isCancelled: " + isCancelled + ", stage: " + stage,
t);
}
// When we're encoding we've already notified our callback and it isn't safe to do so again.
if (stage != Stage.ENCODE) {
throwables.add(t);
notifyFailed();
}
if (!isCancelled) {
throw t;
}
throw t;
} finally {
// Keeping track of the fetcher here and calling cleanup is excessively paranoid, we call
// close in all cases anyway.
if (localFetcher != null) {
localFetcher.cleanup();
}
GlideTrace.endSection();
}
}
在 run 方法中兢哭,當(dāng)前任務(wù)沒(méi)有被取消的話,會(huì)進(jìn)入到 runWrapped() 方法丑搔。
private void runWrapped() {
switch (runReason) {
case INITIALIZE: // 一進(jìn)來(lái)是 INITIALIZE 狀態(tài)的
stage = getNextStage(Stage.INITIALIZE); // 獲取 Stage.INITIALIZE 的下一步
currentGenerator = getNextGenerator(); // 根據(jù)獲取到的 stage 來(lái)選擇不同的 Generator
runGenerators(); // 運(yùn)行 Generator
break;
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
case DECODE_DATA:
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
這里一開(kāi)始進(jìn)入的時(shí)候厦瓢,runReason 是 INITIALIZE 狀態(tài)的。在 getNextStage 中啤月,因?yàn)闆](méi)有圖片緩存所以得到是的 Stage.SOURCE 。那么就接著到了 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);
}
}
stage 是 SOURCE 谎仲,所以獲取到的就是 SourceGenerator 。接著就調(diào)用 runGenerators() 刨仑。
private void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
while (!isCancelled
&& currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) { // 這里執(zhí)行 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.
}
會(huì)先去執(zhí)行 currentGenerator.startNext() 郑诺。所以接著就跳轉(zhuǎn)到 SourceGenerator.startNext() 。
SourceGenerator
@Override
public boolean startNext() {
// 一開(kāi)始肯定沒(méi)有數(shù)據(jù)來(lái)緩存的杉武,所以往下走
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()) {
// 使用 DecodeHelper 的 getLoadData() 方法從注冊(cè)的映射表中找出當(dāng)前的圖片類(lèi)型對(duì)應(yīng)的 ModelLoader辙诞;
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
// 這里調(diào)用 ModelLoader 中的 fetcher 去加載數(shù)據(jù)
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
因?yàn)橹拔覀兊南敕ㄊ羌虞d網(wǎng)絡(luò)上的 url 圖片,所以這里的 loadData 就對(duì)應(yīng)著 HttpGlideUrlLoader
轻抱, fetcher 就是 HttpUrlFetcher 飞涂。
HttpUrlFetcher
@Override
public void loadData(
@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
long startTime = LogTime.getLogTime();
try {
// 在這里獲取網(wǎng)絡(luò)上的圖片數(shù)據(jù)
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
// 回調(diào) onDataReady
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));
}
}
}
loadDataWithRedirects 中會(huì)去調(diào)用 HttpURLConnection 加載網(wǎng)絡(luò)上的圖片數(shù)據(jù)。
加載完之后祈搜,會(huì)回調(diào) onDataReady 方法较店。這個(gè)回調(diào)一直從 HttpUrlFetcher 中一直回調(diào)到 SourceGenerator 中。所以下面就來(lái)看看 SourceGenerator.onDataReady
SourceGenerator
@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);
}
}
在 onDataReady 中容燕,會(huì)去判斷如果 data 不為空并且磁盤(pán)緩存可以緩存的情況下梁呈,會(huì)調(diào)用 cb.reschedule(); 。這其實(shí)是調(diào)用了 DecodeJob 的 reschedule 方法蘸秘。
DecodeJob
@Override
public void reschedule() {
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
callback.reschedule(this);
}
在這里官卡,設(shè)置 runReason 為 RunReason.SWITCH_TO_SOURCE_SERVICE 蝗茁,這很關(guān)鍵,在下面代碼中會(huì)用到寻咒。然后再調(diào)用 callback.reschedule(this) 评甜。其實(shí)就是調(diào)用了 EngineJob 的 reschedule 方法。
EngineJob
@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);
}
reschedule 方法擺明了就是讓 DecodeJob 把 run 方法再跑一遍仔涩。之前說(shuō)過(guò)忍坷,DecodeJob 的 run 方法里面大部分的邏輯其實(shí)是在 runWrapped 中的。
DecodeJob
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);
}
}
這代碼很熟悉熔脂,之前我們的流程到過(guò)這里佩研。不同的是,之前的 runReason 是 INITIALIZE 霞揉。而現(xiàn)在的 runReason 是 SWITCH_TO_SOURCE_SERVICE 旬薯。
接著 SWITCH_TO_SOURCE_SERVICE 的邏輯是直接調(diào)用 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.
}
這里的 currentGenerator 還是之前的 SourceGenerator 适秩,所以還是調(diào)用 SourceGenerator.startNext 绊序。
SourceGenerator
@Override
public boolean startNext() {
// 不同的是,這里的 dataToCache 不再是空的了秽荞,而是之前從網(wǎng)絡(luò)上下載獲取到的 InputStream
// 所以這里會(huì)去走創(chuàng)建緩存的邏輯
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;
}
這次調(diào)用 SourceGenerator.startNext 其實(shí)是建立磁盤(pán)緩存骤公,直接來(lái)看 cacheData 方法。
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 創(chuàng)建一個(gè)對(duì)象阶捆,所以 sourceCacheGenerator 不再是 null
sourceCacheGenerator =
new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
}
這里的主要邏輯是構(gòu)建一個(gè)用于將數(shù)據(jù)緩存到磁盤(pán)上面的 DataCacheGenerator。DataCacheGenerator 的流程基本與 SourceGenerator 一致钦听,也就是根據(jù)資源文件的類(lèi)型找到 ModelLoader洒试,然后使用 DataFetcher 加載緩存的資源。與之前不同的是朴上,這次是用 DataFecher 來(lái)加載 File 類(lèi)型的資源垒棋。也就是說(shuō),當(dāng)我們從網(wǎng)絡(luò)中拿到了數(shù)據(jù)之后 Glide 會(huì)先將其緩存到磁盤(pán)上面痪宰,然后再?gòu)拇疟P(pán)上面讀取圖片并將其顯示到控件上面叼架。所以,當(dāng)從網(wǎng)絡(luò)打開(kāi)了輸入流之后 SourceGenerator 的任務(wù)基本結(jié)束了酵镜,而后的顯示的任務(wù)都由 DataCacheGenerator 來(lái)完成碉碉。
再回過(guò)頭來(lái)看看 SourceGenerator.startNext 方法,在 cacheData 后面會(huì)對(duì) sourceCacheGenerator 進(jìn)行判斷淮韭。由于上面已經(jīng)把 sourceCacheGenerator 對(duì)象 new 出來(lái)了垢粮。所以接著就直接走 DataCacheGenerator 的 startNext 方法了。所以上面這段話就很好理解了靠粪。
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
那么蜡吧,這里我們對(duì) DataCacheGenerator 的邏輯就省略了毫蚓。DataCacheGenerator 中的流程最終會(huì)走到
ByteBufferFetcher 。
之前的 SourceGenerator 對(duì)應(yīng)著 HttpUrlFetcher 昔善,而 DataCacheGenerator 對(duì)應(yīng)著 ByteBufferFetcher 元潘。
ByteBufferFetcher
@Override
public void loadData(
@NonNull Priority priority, @NonNull DataCallback<? super ByteBuffer> callback) {
ByteBuffer result;
try {
// 把圖片的字節(jié)流再?gòu)拇疟P(pán)緩存中讀取出來(lái)
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;
}
// 回調(diào)給 DataCacheGenerator 的 onDataReady 方法
callback.onDataReady(result);
}
磁盤(pán)緩存好之后,再?gòu)奈募凶x取圖片的字節(jié)流君仆◆娓牛回調(diào)給 DataCacheGenerator
DataCacheGenerator
@Override
public void onDataReady(Object data) {
cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);
}
DataCacheGenerator 會(huì)回調(diào) DecodeJob 的 onDataFetcherReady 方法。
DecodeJob
@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();
}
}
}
runReason 已經(jīng)改成 RunReason.DECODE_DATA 返咱,說(shuō)明已經(jīng)進(jìn)行到解碼圖片數(shù)據(jù)的環(huán)節(jié)了钥庇。
接著調(diào)用 decodeFromRetrievedData 。
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);
}
// 圖片資源獲取到后咖摹,通知已經(jīng)任務(wù)完成
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
這里我們可以看下评姨,當(dāng) resource 最終獲取到后,是通過(guò) notifyEncodeAndRelease 來(lái)通知任務(wù)完成的萤晴。這在后面的代碼解析中會(huì)講到吐句。
現(xiàn)在,我們就來(lái)看看關(guān)鍵的邏輯店读,接著調(diào)用 decodeFromData 嗦枢。
private <Data> Resource<R> decodeFromData(
DataFetcher<?> fetcher, Data data, DataSource dataSource) throws GlideException {
try {
if (data == null) {
return null;
}
long startTime = LogTime.getLogTime();
Resource<R> result = decodeFromFetcher(data, dataSource);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded result " + result, startTime);
}
return result;
} finally {
fetcher.cleanup();
}
}
調(diào)用 decodeFromFetcher 方法。
@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);
}
調(diào)用了 runLoadPath 两入。
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();
}
}
調(diào)用 path.load 方法净宵。
LoadPath
public Resource<Transcode> load(
DataRewinder<Data> rewinder,
@NonNull Options options,
int width,
int height,
DecodePath.DecodeCallback<ResourceType> decodeCallback)
throws GlideException {
List<Throwable> throwables = Preconditions.checkNotNull(listPool.acquire());
try {
return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);
} finally {
listPool.release(throwables);
}
}
直接調(diào)用 loadWithExceptionList 方法。
private Resource<Transcode> loadWithExceptionList(
DataRewinder<Data> rewinder,
@NonNull Options options,
int width,
int height,
DecodePath.DecodeCallback<ResourceType> decodeCallback,
List<Throwable> exceptions)
throws GlideException {
Resource<Transcode> result = null;
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = decodePaths.size(); i < size; i++) {
DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
try {
result = path.decode(rewinder, width, height, options, decodeCallback);
} catch (GlideException e) {
exceptions.add(e);
}
if (result != null) {
break;
}
}
if (result == null) {
throw new GlideException(failureMessage, new ArrayList<>(exceptions));
}
return result;
}
然后會(huì)調(diào)用 DecodePath 的 decode 方法裹纳。
DecodePath
public Resource<Transcode> decode(
DataRewinder<DataType> rewinder,
int width,
int height,
@NonNull Options options,
DecodeCallback<ResourceType> callback)
throws GlideException {
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
return transcoder.transcode(transformed, options);
}
在 decode 中做了三件事:
- decodeResource 將原始數(shù)據(jù)轉(zhuǎn)換成我們?cè)紙D片的過(guò)程;
- callback.onResourceDecoded 是當(dāng)?shù)玫搅嗽紙D片之后對(duì)圖片繼續(xù)處理過(guò)程;
- transcoder.transcode 會(huì)使用 BitmapDrawableTranscoder 包裝一層,即對(duì) Drawable 進(jìn)行延遲初始化處理紧武。
那么我們接著跟進(jìn) decodeResource 方法剃氧。
@NonNull
private Resource<ResourceType> decodeResource(
DataRewinder<DataType> rewinder, int width, int height, @NonNull Options options)
throws GlideException {
List<Throwable> exceptions = Preconditions.checkNotNull(listPool.acquire());
try {
return decodeResourceWithList(rewinder, width, height, options, exceptions);
} finally {
listPool.release(exceptions);
}
}
主要邏輯在 decodeResourceWithList 中。
@NonNull
private Resource<ResourceType> decodeResourceWithList(
DataRewinder<DataType> rewinder,
int width,
int height,
@NonNull Options options,
List<Throwable> exceptions)
throws GlideException {
Resource<ResourceType> result = null;
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = decoders.size(); i < size; i++) {
ResourceDecoder<DataType, ResourceType> decoder = decoders.get(i);
try {
DataType data = rewinder.rewindAndGet();
if (decoder.handles(data, options)) {
data = rewinder.rewindAndGet();
result = decoder.decode(data, width, height, options);
}
// Some decoders throw unexpectedly. If they do, we shouldn't fail the entire load path, but
// instead log and continue. See #2406 for an example.
} catch (IOException | RuntimeException | OutOfMemoryError e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Failed to decode data for " + decoder, e);
}
exceptions.add(e);
}
if (result != null) {
break;
}
}
if (result == null) {
throw new GlideException(failureMessage, new ArrayList<>(exceptions));
}
return result;
}
ResourceDecoder 具有多個(gè)實(shí)現(xiàn)類(lèi)阻星,比如 BitmapDrawableDecoder朋鞍、ByteBufferBitmapDecoder等。從名字也可以看出來(lái)是用來(lái)將一個(gè)類(lèi)型轉(zhuǎn)換成另一個(gè)類(lèi)型的妥箕。
在這里會(huì)使用 ByteBufferBitmapDecoder 來(lái)將 ByteBuffer 專(zhuān)成 Bitmap 滥酥。
ByteBufferBitmapDecoder
@Override
public Resource<Bitmap> decode(
@NonNull ByteBuffer source, int width, int height, @NonNull Options options)
throws IOException {
InputStream is = ByteBufferUtil.toStream(source);
return downsampler.decode(is, width, height, options);
}
它最終會(huì)在 Downsampler 的 decodeStream() 方法中調(diào)用 BitmapFactory 的 decodeStream() 方法來(lái)從輸入流中得到 Bitmap。
在 Downsampler 內(nèi)部還會(huì)維持一個(gè) BitmapPool 畦幢,用來(lái)復(fù)用 Bitmap 坎吻。有興趣的同學(xué)可以看下這一塊的代碼,這里就不過(guò)多展示了宇葱。
接下來(lái)瘦真,就來(lái)看看上面 callback.onResourceDecoded 的邏輯刊头。callback.onResourceDecoded 會(huì)調(diào)用 DecodeJob.onResourceDecoded 方法。
DecodeJob
@Synthetic
@NonNull
<Z> Resource<Z> onResourceDecoded(DataSource dataSource, @NonNull Resource<Z> decoded) {
@SuppressWarnings("unchecked")
Class<Z> resourceSubClass = (Class<Z>) decoded.get().getClass();
Transformation<Z> appliedTransformation = null;
Resource<Z> transformed = decoded;
if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
transformed = appliedTransformation.transform(glideContext, decoded, width, height);
}
// TODO: Make this the responsibility of the Transformation.
if (!decoded.equals(transformed)) {
decoded.recycle();
}
final EncodeStrategy encodeStrategy;
final ResourceEncoder<Z> encoder;
if (decodeHelper.isResourceEncoderAvailable(transformed)) {
encoder = decodeHelper.getResultEncoder(transformed);
encodeStrategy = encoder.getEncodeStrategy(options);
} else {
encoder = null;
encodeStrategy = EncodeStrategy.NONE;
}
Resource<Z> result = transformed;
boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey);
if (diskCacheStrategy.isResourceCacheable(
isFromAlternateCacheKey, dataSource, encodeStrategy)) {
if (encoder == null) {
throw new Registry.NoResultEncoderAvailableException(transformed.get().getClass());
}
final Key key;
switch (encodeStrategy) {
case SOURCE:
key = new DataCacheKey(currentSourceKey, signature);
break;
case TRANSFORMED:
key =
new ResourceCacheKey(
decodeHelper.getArrayPool(),
currentSourceKey,
signature,
width,
height,
appliedTransformation,
resourceSubClass,
options);
break;
default:
throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy);
}
LockedResource<Z> lockedResult = LockedResource.obtain(transformed);
deferredEncodeManager.init(key, encoder, lockedResult);
result = lockedResult;
}
return result;
}
主要的邏輯是根據(jù)我們?cè)O(shè)置的參數(shù)進(jìn)行變化诸尽。也就是說(shuō)原杂,如果我們使用了 centerCrop 等參數(shù),那么這里將會(huì)對(duì)其進(jìn)行處理您机。這里的 Transformation 是一個(gè)接口穿肄,它的一系列的實(shí)現(xiàn)都是對(duì)應(yīng)于 scaleType 等參數(shù)的。
到了這里际看, Glide 所有加載圖片咸产、處理圖片的邏輯都講完了。剩下的仿村,就是將圖片顯示到 ImageView 上面了锐朴。
我們?cè)倩剡^(guò)頭來(lái)看之前講到 DecodeJob.notifyEncodeAndRelease 方法
DecodeJob
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();
}
}
// Call onEncodeComplete outside the finally block so that it's not called if the encode process
// throws.
onEncodeComplete();
}
來(lái)看 notifyComplete(result, dataSource); 方法
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
setNotifiedOrThrow();
callback.onResourceReady(resource, dataSource);
}
回調(diào) EngineJob 的 onResourceReady 方法。
EngineJob
@Override
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
synchronized (this) {
this.resource = resource;
this.dataSource = dataSource;
}
notifyCallbacksOfResult();
}
關(guān)鍵在 notifyCallbacksOfResult 中蔼囊。
@Synthetic
void notifyCallbacksOfResult() {
ResourceCallbacksAndExecutors copy;
Key localKey;
EngineResource<?> localResource;
synchronized (this) {
stateVerifier.throwIfRecycled();
if (isCancelled) {
// TODO: Seems like we might as well put this in the memory cache instead of just recycling
// it since we've gotten this far...
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, key, resourceListener);
// Hold on to resource for duration of our callbacks below so we don't recycle it in the
// middle of notifying if it synchronously released by one of the callbacks. Acquire it under
// a lock here so that any newly added callback that executes before the next locked section
// below can't recycle the resource before we call the callbacks.
hasResource = true;
copy = cbs.copy();
incrementPendingCallbacks(copy.size() + 1);
localKey = key;
localResource = engineResource;
}
engineJobListener.onEngineJobComplete(this, localKey, localResource);
for (final ResourceCallbackAndExecutor entry : copy) {
entry.executor.execute(new CallResourceReady(entry.cb));
}
decrementPendingCallbacks();
}
在代碼的最后焚志,
for (final ResourceCallbackAndExecutor entry : copy) {
entry.executor.execute(new CallResourceReady(entry.cb));
}
可以看到這里會(huì)執(zhí)行 CallResourceReady 。
CallResourceReady
private class CallResourceReady implements Runnable {
private final ResourceCallback cb;
CallResourceReady(ResourceCallback cb) {
this.cb = cb;
}
@Override
public void run() {
// Make sure we always acquire the request lock, then the EngineJob lock to avoid deadlock
// (b/136032534).
synchronized (cb) {
synchronized (EngineJob.this) {
if (cbs.contains(cb)) {
// Acquire for this particular callback.
engineResource.acquire();
callCallbackOnResourceReady(cb);
removeCallback(cb);
}
decrementPendingCallbacks();
}
}
}
}
@Synthetic
@GuardedBy("this")
void callCallbackOnResourceReady(ResourceCallback cb) {
try {
// This is overly broad, some Glide code is actually called here, but it's much
// simpler to encapsulate here than to do so at the actual call point in the
// Request implementation.
cb.onResourceReady(engineResource, dataSource);
} catch (Throwable t) {
throw new CallbackException(t);
}
}
最后還是用回調(diào)調(diào)用了 SingleRequest 的 onResourceReady 方法畏鼓。
SingleRequest
@Override
public synchronized void onResourceReady(Resource<?> resource, DataSource dataSource) {
stateVerifier.throwIfRecycled();
loadStatus = null;
if (resource == null) {
GlideException exception =
new GlideException(
"Expected to receive a Resource<R> with an "
+ "object of "
+ transcodeClass
+ " inside, but instead got null.");
onLoadFailed(exception);
return;
}
Object received = resource.get();
if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
releaseResource(resource);
GlideException exception =
new GlideException(
"Expected to receive an object of "
+ transcodeClass
+ " but instead"
+ " got "
+ (received != null ? received.getClass() : "")
+ "{"
+ received
+ "} inside"
+ " "
+ "Resource{"
+ resource
+ "}."
+ (received != null
? ""
: " "
+ "To indicate failure return a null Resource "
+ "object, rather than a Resource object containing null data."));
onLoadFailed(exception);
return;
}
if (!canSetResource()) {
releaseResource(resource);
// We can't put the status to complete before asking canSetResource().
status = Status.COMPLETE;
return;
}
onResourceReady((Resource<R>) resource, (R) received, dataSource);
}
最后調(diào)用 onResourceReady((Resource<R>) resource, (R) received, dataSource);
private synchronized void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
// We must call isFirstReadyResource before setting status.
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
if (glideContext.getLogLevel() <= Log.DEBUG) {
Log.d(
GLIDE_TAG,
"Finished loading "
+ result.getClass().getSimpleName()
+ " from "
+ dataSource
+ " for "
+ model
+ " with size ["
+ width
+ "x"
+ height
+ "] in "
+ LogTime.getElapsedMillis(startTime)
+ " ms");
}
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);
// 重點(diǎn)在這里=闯辍!T平谩膳沽!
if (!anyListenerHandledUpdatingTarget) {
Transition<? super R> animation = animationFactory.build(dataSource, isFirstResource);
target.onResourceReady(result, animation);
}
} finally {
isCallingCallbacks = false;
}
notifyLoadSuccess();
}
發(fā)現(xiàn)上面的代碼調(diào)用了 target.onResourceReady(result, animation);
這里的 target 一般都是 ImageViewTarget 。ImageViewTarget 是個(gè)抽象類(lèi)让禀。
ImageViewTarget
@Override
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) {
// Order matters here. Set the resource first to make sure that the Drawable has a valid and
// non-null Callback before starting it.
setResource(resource);
maybeUpdateAnimatable(resource);
}
setResource(resource) 是抽象方法挑社,我們到子類(lèi)中看看。我們挑 DrawableImageViewTarget 來(lái)看看吧巡揍。
DrawableImageViewTarget
@Override
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
終于痛阻,我們看到了 ImageView.setImageDrawable 來(lái)顯示圖片了,不容易啊腮敌。
總結(jié)
真的沒(méi)想到阱当,短短的一句 Glide.with(context).load("http://www.xxxx.com/xx.jpg").into(imageView) 代碼內(nèi)部竟然隱藏著如此龐大的邏輯。相信你看完這一系列的文章糜工,對(duì) Glide 會(huì)刮目相看吧弊添。
當(dāng)然,本系列還有很多 Glide 中沒(méi)講到的知識(shí)點(diǎn)捌木,比如緩存具體的應(yīng)用等油坝,如果想了解的同學(xué)可以自行去閱讀下源碼。