1. Glide總覽
Glide圖片加載框架從總體來看采用三段論來讓用戶輕松加載圖片腥光。
- with()方法主要通過創(chuàng)建一個(gè)空白的Fragment蓋在展示的頁面上笆搓,感知展示頁面的生命周期,來動(dòng)態(tài)調(diào)整Glide內(nèi)部請(qǐng)求等業(yè)務(wù)去扣。詳細(xì)參見Glide源碼分析-生命周期管理
- load()方法完成通用請(qǐng)求的構(gòu)建
- into()方法主要維護(hù)這運(yùn)行隊(duì)列和等待隊(duì)列,當(dāng)生命周期改變的時(shí)候藏姐,動(dòng)態(tài)清除一些不必要加載的請(qǐng)求。同時(shí)還維護(hù)著活動(dòng)緩存和內(nèi)存緩存该贾,多級(jí)緩存有利于節(jié)約資源和加載效率羔杨,最后如果沒有緩存就采用網(wǎng)絡(luò)請(qǐng)求的方式加載請(qǐng)求的內(nèi)容。
2. with()
with()方法的詳細(xì)分析可以參見上一篇Glide的敘述:Glide源碼分析-生命周期管理杨蛋。
總體來講兜材,with()方法中發(fā)生了如下事情:
- Glide類:主要做一些 init 工作,比如緩存逞力,線程池曙寡,復(fù)用池的構(gòu)建等等。
- RequestManagerRetriever類:主要是獲得一個(gè)
RequestManager
請(qǐng)求管理類寇荧,然后綁定一個(gè) Fragment - SupportRequestManagerFragment 類:用于管理請(qǐng)求的生命周期举庶。
- RequestManager:主要用于對(duì)請(qǐng)求的管理封裝。
3. load()
load()流程其實(shí)不是很復(fù)雜揩抡,主要做了通用資源類型的設(shè)置選項(xiàng)和啟動(dòng)負(fù)載户侥,在RequestBuilder 中完成。流程如下圖:
RequestBuilder : 這是一個(gè)通用請(qǐng)求構(gòu)建類峦嗤,可以處理通用資源類型的設(shè)置選項(xiàng)和啟動(dòng)負(fù)載蕊唐。
public class RequestManager implements LifecycleListener,
ModelTypes<RequestBuilder<Drawable>> {
.....
public RequestBuilder<Drawable> load(@Nullable String string) {
//這里調(diào)用 Drawable 圖片加載請(qǐng)求器為其加載
return asDrawable().load(string);
}
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Uri uri) {
return asDrawable().load(uri);
}
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable File file) {
return asDrawable().load(file);
}
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
}
public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {
public RequestBuilder<TranscodeType> load(@Nullable String string) {
return loadGeneric(string);
}
// 描述加載的數(shù)據(jù)源-這里可以看做是我們傳遞進(jìn)來的 http://xxxx.png
@Nullable private Object model;
// 描述這個(gè)請(qǐng)求是否已經(jīng)添加了加載的數(shù)據(jù)源
private boolean isModelSet;
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
// model是我們需要加載的數(shù)據(jù)源,可能為null烁设,為了保證model一定被賦值替梨,此處引進(jìn)isModelSet來保證。
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
}
到這里 RequestBuilder 就構(gòu)建好了装黑, RequestBuilder構(gòu)建出來副瀑,接下來就是重頭戲into()。
4.into()
into()是所有流程中最最復(fù)雜的方法恋谭,有60多個(gè)步驟糠睡,此處我們分成幾個(gè)環(huán)節(jié)來一一分析流程。
分析之前箕别,我們腦子中一定記住從最初是狀態(tài)開始翻代碼铜幽,最初始狀態(tài)就是:拿到了url滞谢,各級(jí)緩存都沒有資源串稀,需要從網(wǎng)上下載資源,然后加載到控件中狮杨。
into()整體流程圖在Glide工程中src目錄中母截。
4.1 確定控件大小
我們從網(wǎng)上加載的inputstram不可能是下載多少就加載多少,這樣內(nèi)存可能爆掉橄教,或者效率很低清寇,我們應(yīng)該按需加載喘漏,控件多大,我們就將圖片壓縮到多大加載华烟。
@NonNull
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
// 根據(jù) ImageView 布局中的 scaleType 來重構(gòu) requestOptions
BaseRequestOptions<?> requestOptions = this;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
//如果在 xml ImageView 節(jié)點(diǎn)中 沒有設(shè)置 scaleType 那么默認(rèn)在構(gòu)造函數(shù)中進(jìn)行了初始化為 mScaleType = ScaleType.FIT_CENTER;
switch (view.getScaleType()) {
.....
case FIT_CENTER:
case FIT_START:
case FIT_END:
//這里用到了克卖媛酢(原型設(shè)計(jì)模式),選擇一個(gè)"居中合適"顯示的方案
requestOptions = requestOptions.clone().optionalFitCenter();
break;
....
}
}
//調(diào)用 into 重載函數(shù)盔夜,創(chuàng)建一個(gè) ViewTarget
return into(
//調(diào)用 buildImageViewTarget 構(gòu)建一個(gè) ImageView 類型的 Target(Bitmap/Drawable)
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
Executors.mainThreadExecutor());
}
上面代碼就兩大步:
第一步:先拿到當(dāng)前 ImageView getScaleType 類型的屬性负饲,然后重新 clone 一個(gè)進(jìn)行配置;
第二步:調(diào)用 into 重載繼續(xù)構(gòu)建喂链;
先來看下 glideContext.buildImageViewTarget 是怎么構(gòu)建出來 ImageViewTarget 的:
@NonNull
public <X> ViewTarget<ImageView, X> buildImageViewTarget(
@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
//調(diào)用 工廠模式 根據(jù) transcodeClass 生成出一個(gè)對(duì)應(yīng)的 ImageViewTarget
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
public class ImageViewTargetFactory {
@NonNull
@SuppressWarnings("unchecked")
public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view,
@NonNull Class<Z> clazz) {
//如果目標(biāo)的編碼類型屬于 Bitmap 那么就創(chuàng)建一個(gè) Bitmap 類型的 ImageViewTarget
if (Bitmap.class.equals(clazz)) {
return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
////如果目標(biāo)的編碼類型屬于 Drawable 那么就創(chuàng)建一個(gè) Drawable 類型的 ImageViewTarget
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
}
上面 生產(chǎn) Target 的時(shí)候注意一下返十,只要調(diào)用了 asBitmap
才會(huì)執(zhí)行生產(chǎn) BitmapImageViewTarget ,所以這里我們關(guān)注 Drawable 類型就行了,我們就先簡單看看這個(gè) target 內(nèi)部怎么實(shí)現(xiàn)的椭微,因?yàn)樽詈螽?dāng)拿到請(qǐng)求資源之后洞坑,會(huì)在target中設(shè)置資源到view中。
public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {
public DrawableImageViewTarget(ImageView view) {
super(view);
}
@SuppressWarnings({"unused", "deprecation"})
@Deprecated
public DrawableImageViewTarget(ImageView view, boolean waitForLayout) {
super(view, waitForLayout);
}
@Override
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
}
從上面代碼可以知道 DrawableImageViewTarget 繼承的是 ImageViewTarget 重寫的 setResource 函數(shù)蝇率,實(shí)現(xiàn)了顯示 Drawable 圖片的邏輯迟杂。
回到主流程,into()重載方法中本慕。
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
Preconditions.checkNotNull(target);
//這里的 isModelSet 是在 load 的時(shí)候賦值為 true 的逢慌,所以不會(huì)拋異常,確保了信息源一定被設(shè)置间狂。
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
//為這個(gè) http://xxx.png 生成一個(gè) Glide request 請(qǐng)求
Request request = buildRequest(target, targetListener, options, callbackExecutor);
//相當(dāng)于拿到上一個(gè)請(qǐng)求
Request previous = target.getRequest();
//下面的幾行說明是否與上一個(gè)請(qǐng)求沖突攻泼,一般不用管 直接看下面 else 判斷
// 如果兩個(gè)請(qǐng)求參數(shù)和大小相同,就不用再請(qǐng)求了鉴象,直接返回忙菠。此if中也會(huì)判斷前一個(gè)請(qǐng)求是否在請(qǐng)求中等狀態(tài),此處我們不用關(guān)心纺弊,第一次加載圖片不會(huì)進(jìn)入此if語句牛欢。
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
request.recycle();
if (!Preconditions.checkNotNull(previous).isRunning()) {
previous.begin();
}
return target;
}
//如果是一個(gè)新請(qǐng)求,先清理掉目標(biāo)請(qǐng)求管理
requestManager.clear(target);
//重新為目標(biāo)設(shè)置一個(gè) Glide request 請(qǐng)求
target.setRequest(request);
//最后是調(diào)用 RequestManager 的 track 來執(zhí)行目標(biāo)的 Glide request 請(qǐng)求
requestManager.track(target, request);
return target;
}
以上核心就兩個(gè)點(diǎn):
第一點(diǎn):為 target buildRequest 構(gòu)建一個(gè) Glide request 請(qǐng)求淆游;
第二點(diǎn):將構(gòu)建出來的 Request 交于 RequestManager 來執(zhí)行傍睹;
我們先簡單的來看下怎么構(gòu)建的 Request:
private Request buildRequest(
Target<TranscodeType> target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> requestOptions,
Executor callbackExecutor) {
return buildRequestRecursive(
target,
targetListener,
/*parentCoordinator=*/ null,
transitionOptions,
requestOptions.getPriority(),
requestOptions.getOverrideWidth(),
requestOptions.getOverrideHeight(),
requestOptions,
callbackExecutor);
}
private Request obtainRequest(
Target<TranscodeType> target,
RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> requestOptions,
RequestCoordinator requestCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
Executor callbackExecutor) {
return SingleRequest.obtain(
context,
glideContext,
model,
transcodeClass,
requestOptions,
overrideWidth,
overrideHeight,
priority,
target,
targetListener,
requestListeners,
requestCoordinator,
glideContext.getEngine(),
transitionOptions.getTransitionFactory(),
callbackExecutor);
}
最后我們發(fā)現(xiàn)是 SingleRequest.obtain
來為我們構(gòu)建的 Request 請(qǐng)求對(duì)象,開始只是初始化一些配置屬性犹菱。
繼續(xù)回到上面看request的track()方法蚌堵。
//這里對(duì)當(dāng)前 class 加了一個(gè)同步鎖避免線程引起的安全性
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
//添加一個(gè)目標(biāo)任務(wù)
targetTracker.track(target);
//執(zhí)行 Glide request
requestTracker.runRequest(request);
}
public void runRequest(@NonNull Request request) {
//添加一個(gè)請(qǐng)求
requests.add(request);
//是否暫停
if (!isPaused) {
//沒有暫停,開始調(diào)用 Request begin 執(zhí)行
request.begin();
} else {
//如果調(diào)用了 暫停宁炫,清理請(qǐng)求
request.clear();
pendingRequests.add(request);
}
}
上面的邏輯是先為 requests
添加一個(gè)請(qǐng)求乏悄,看看是否是停止?fàn)顟B(tài),如果不是就調(diào)用 request.begin();
執(zhí)行陕凹。
這里的 Request
是一個(gè)接口悍抑,通過之前我們講到 buildRequest
函數(shù)可知 Request
的實(shí)現(xiàn)類是 SingleRequest
我們就直接看它的 begin
函數(shù).
@Override
public synchronized void begin() {
assertNotCallingCallbacks();
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
if (model == null) {
//檢查外部調(diào)用的尺寸是否有效
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
width = overrideWidth;
height = overrideHeight;
}
//失敗的回調(diào)
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 (status == Status.COMPLETE) {
//表示資源準(zhǔn)備好了
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
status = Status.WAITING_FOR_SIZE;
//這里表示大小已經(jīng)準(zhǔn)備好了
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
// 需要加載的尺寸已經(jīng)測(cè)量好了鳄炉,接下里就開始加載引擎。
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
//這里是剛剛開始執(zhí)行的回調(diào)搜骡,相當(dāng)于顯示開始的進(jìn)度
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));
}
}
@Override
public void onSizeReady(int width, int height) {
......
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);
......
}
}
4.2 從不同緩存中搜尋資源
public synchronized <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) {
//拿到緩存或者請(qǐng)求的 key
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
//根據(jù) key 拿到活動(dòng)緩存中的資源
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
//如果 ActiveResources 活動(dòng)緩存中有就回調(diào)出去
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
return null;
}
//嘗試從 LruResourceCache 中找尋這個(gè)資源
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
//如果內(nèi)存緩存 Lru 中資源存在回調(diào)出去
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
return null;
}
//------------- 走到這里說明活動(dòng)緩存 跟內(nèi)存 緩存都沒有找到 -----------
//根據(jù) Key 看看緩存中是否正在執(zhí)行
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
//如果正在執(zhí)行拂盯,把數(shù)據(jù)回調(diào)出去
current.addCallback(cb, callbackExecutor);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
// -------------- 走到這里說明是一個(gè)新的任務(wù) ---------------
// -------------- 構(gòu)建新的請(qǐng)求任務(wù) ---------------
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);
//把當(dāng)前需要執(zhí)行的 key 添加進(jìn)緩存
jobs.put(key, engineJob);
//執(zhí)行任務(wù)的回調(diào)
engineJob.addCallback(cb, callbackExecutor);
//開始執(zhí)行。
engineJob.start(decodeJob);
return new LoadStatus(cb, engineJob);
}
通過 engine.load
這個(gè)函數(shù)里面的邏輯记靡,我們可以總結(jié)3點(diǎn):
先構(gòu)建請(qǐng)求或者緩存 KEY ;
根據(jù) KEY 從內(nèi)存緩存中查找對(duì)應(yīng)的資源數(shù)據(jù)(ActiveResources(活動(dòng)緩存磕仅,內(nèi)部是一個(gè) Map 用弱引用持有),LruResourceCache),如果有就回調(diào) 對(duì)應(yīng)監(jiān)聽的 onResourceReady 表示數(shù)據(jù)準(zhǔn)備好了簸呈。
-
從執(zhí)行緩存中查找對(duì)應(yīng) key 的任務(wù)
如果找到了榕订,就說明已經(jīng)正在執(zhí)行了,不用重復(fù)執(zhí)行蜕便。
沒有找到劫恒,通過 EngineJob.start 開啟一個(gè)新的請(qǐng)求任務(wù)執(zhí)行。
為了看主線轿腺,時(shí)刻記住開始強(qiáng)調(diào)的两嘴,所有緩存中都沒有數(shù)據(jù)!直接看 EngineJob.start
public synchronized void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
//拿到 Glide 執(zhí)行的線程池
GlideExecutor executor = decodeJob.willDecodeFromCache()
? diskCacheExecutor
: getActiveSourceExecutor();
//開始執(zhí)行
executor.execute(decodeJob);
}
通過 DecodeJob
源碼得知族壳,它是實(shí)現(xiàn)的 Runnable
接口憔辫,這里 GlideExecutor 線程池開始執(zhí)行,就會(huì)啟動(dòng) DecodeJob 的 run 函數(shù)仿荆。
細(xì)心的小伙伴可以看出engineJob之后就decodeJob贰您,不難想出,這兩個(gè)任務(wù)之間一定有獲取到資源的過程拢操。而decodeJob包裹了engineJob锦亦,decodeJob是一個(gè)runnable,所以不難猜出會(huì)在線程池中網(wǎng)絡(luò)請(qǐng)求資源令境,并且解碼資源杠园。下面我們就來看看是如何加載資源和解碼的。
4.3 尋找資源加載器
此處不得不佩服框架考慮得的確很周全舔庶,我們需要加載資源抛蚁,肯定就需要資源加載器,那么是不是只有一個(gè)加載器呢惕橙,當(dāng)然不是瞧甩,只有一個(gè)加載器的代碼可能是我寫的,哈哈哈吕漂。透露小秘密:框架會(huì)根據(jù)我們load傳入的不同信息源亲配,獲取不同的資源加載器尘应。下面我們來看一下流程惶凝。
上一小節(jié)結(jié)束我們可以看到包裹了engineJob的decodeJob任務(wù)會(huì)在線程池中執(zhí)行吼虎,那么不難想出decodeJob的run()肯定會(huì)被調(diào)用。
class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
Runnable,
Comparable<DecodeJob<?>>,
Poolable {
// 線程執(zhí)行調(diào)用 run
@Override
public void run() {
GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);
DataFetcher<?> localFetcher = currentFetcher;
try {
//是否取消了當(dāng)前請(qǐng)求
if (isCancelled) {
notifyFailed();
return;
}
//執(zhí)行
runWrapped();
} catch (CallbackException e) {
.....//一些錯(cuò)誤回調(diào)
}
}
分析runWrapped:
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
//獲取資源狀態(tài)
stage = getNextStage(Stage.INITIALIZE);
//根據(jù)當(dāng)前資源狀態(tài)苍鲜,獲取資源執(zhí)行器
currentGenerator = getNextGenerator();
//執(zhí)行
runGenerators();
break;
...
}
}
private Stage getNextStage(Stage current) {
switch (current) {
case INITIALIZE:
//如果外部調(diào)用配置了資源緩存策略思灰,那么返回 Stage.RESOURCE_CACHE
//否則繼續(xù)調(diào)用 Stage.RESOURCE_CACHE 執(zhí)行。
return diskCacheStrategy.decodeCachedResource()
? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
case RESOURCE_CACHE:
//如果外部配置了源數(shù)據(jù)緩存混滔,那么返回 Stage.DATA_CACHE
//否則繼續(xù)調(diào)用 getNextStage(Stage.DATA_CACHE)
return diskCacheStrategy.decodeCachedData()
? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
case DATA_CACHE:
//如果只能從緩存中獲取數(shù)據(jù)洒疚,則直接返回 FINISHED,否則坯屿,返回SOURCE油湖。
//意思就是一個(gè)新的資源
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}
通過上面代碼可以知道,我們?cè)谡屹Y源的執(zhí)行器领跛,這里由于我們沒有在外部配置緩存策略所以乏德,直接從源數(shù)據(jù)加載,看下面代碼:
private DataFetcherGenerator getNextGenerator() {
switch (stage) {
//從資源緩存執(zhí)行器
case RESOURCE_CACHE:
return new ResourceCacheGenerator(decodeHelper, this);
//源數(shù)據(jù)磁盤緩存執(zhí)行器
case DATA_CACHE:
return new DataCacheGenerator(decodeHelper, this);
//什么都沒有配置吠昭,源數(shù)據(jù)的執(zhí)行器
case SOURCE:
return new SourceGenerator(decodeHelper, this);
case FINISHED:
return null;
default:
throw new IllegalStateException("Unrecognized stage: " + stage);
}
}
由于我們什么都沒有配置喊括,返回的是 SourceGenerator
源數(shù)據(jù)執(zhí)行器。繼續(xù)下面代碼執(zhí)行:
private void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
//判斷是否取消矢棚,是否開始
//調(diào)用 DataFetcherGenerator.startNext() 判斷是否是屬于開始執(zhí)行的任務(wù)
while (!isCancelled && currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
....
}
上面代碼先看 currentGenerator.startNext()
這句代碼郑什,DataFetcherGenerator
是一個(gè)抽象類,那么這里執(zhí)行的實(shí)現(xiàn)類是哪一個(gè)蒲肋,可以參考下面說明:
狀態(tài)標(biāo)記 | 作用 | 執(zhí)行器 |
---|---|---|
Stage.RESOURCE_CACHE | 從磁盤中獲取緩存的資源數(shù)據(jù) | ResourceCacheGenerator |
Stage.DATA_CACHE | 從磁盤中獲取緩存的源數(shù)據(jù) | DataCacheGenerator |
Stage.SOURCE | 一次新的請(qǐng)求任務(wù) | SourceGenerator |
因?yàn)檫@里我們沒有配置緩存蘑拯,那么直接看 SourceGenerator
@Override
public boolean startNext() {
...
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
//獲取一個(gè) ModelLoad 加載器
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
//使用加載器中的 fetcher 根據(jù)優(yōu)先級(jí)加載數(shù)據(jù)
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
代碼邏輯很簡單,首先獲取加載器兜粘,然后從加載器中使用fetcher來加載數(shù)據(jù)就可以了强胰。
List<LoadData<?>> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
//從 Glide 注冊(cè)的 Model 來獲取加載器(注冊(cè)是在 Glide 初始化的時(shí)候通過 registry
// .append()添加的)
List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
for (int i = 0, size = modelLoaders.size(); i < size; i++) {
ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
LoadData<?> current =
// 構(gòu)建加載器。modelLoader是一個(gè)接口妹沙,需要知道是那個(gè)類的buildLoadData()來構(gòu)建加載器偶洋。看看下面的分析距糖。
modelLoader.buildLoadData(model, width, height, options);
//如果加載器器不為空玄窝,那么添加進(jìn)臨時(shí)緩存
if (current != null) {
loadData.add(current);
}
}
}
return loadData;
}
獲取到加載器之后,會(huì)通過加載器中的fetcher加載數(shù)據(jù)悍引,然后我們進(jìn)入loadData()一看恩脂,fetcher是一個(gè)接口(DataFetcher<Data>),實(shí)現(xiàn)類有一大堆(如下圖)趣斤,我們根本沒法知道到底是哪個(gè)來加載數(shù)據(jù)俩块。
此處就是框架設(shè)計(jì)巧妙的一點(diǎn)了,到底是怎么知道哪個(gè)fetcher呢?
此時(shí)我們要回到 Glide 初始的時(shí)候 通過 Registry.append()
添加的S窨J迫!B汀I诱! 下面我們來看看盲厌。
Glide(
@NonNull Context context,
@NonNull Engine engine,
@NonNull MemoryCache memoryCache,
@NonNull BitmapPool bitmapPool,
@NonNull ArrayPool arrayPool,
@NonNull RequestManagerRetriever requestManagerRetriever,
@NonNull ConnectivityMonitorFactory connectivityMonitorFactory,
int logLevel,
@NonNull RequestOptionsFactory defaultRequestOptionsFactory,
@NonNull Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions,
@NonNull List<RequestListener<Object>> defaultRequestListeners,
boolean isLoggingRequestOriginsEnabled,
boolean isImageDecoderEnabledForBitmaps) {
......
registry
.append(Uri.class, InputStream.class, new UrlUriLoader.StreamFactory())
.append(URL.class, InputStream.class, new UrlLoader.StreamFactory())
.append(Uri.class, File.class, new MediaStoreFileLoader.Factory(context))
// 我們load傳入的是一個(gè)url署照,所以我們的fetcher就是HttpGlideUrlLoader。
.append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
.append(byte[].class, ByteBuffer.class, new ByteArrayLoader.ByteBufferFactory())
.append(byte[].class, InputStream.class, new ByteArrayLoader.StreamFactory())
.append(Uri.class, Uri.class, UnitModelLoader.Factory.<Uri>getInstance())
.append(Drawable.class, Drawable.class, UnitModelLoader.Factory.<Drawable>getInstance())
.append(Drawable.class, Drawable.class, new UnitDrawableDecoder())
/* Transcoders */
.register(Bitmap.class, BitmapDrawable.class, new BitmapDrawableTranscoder(resources))
.register(Bitmap.class, byte[].class, bitmapBytesTranscoder)
.register(
Drawable.class,
byte[].class,
new DrawableBytesTranscoder(
bitmapPool, bitmapBytesTranscoder, gifDrawableBytesTranscoder))
.register(GifDrawable.class, byte[].class, gifDrawableBytesTranscoder);
......
}
從Glide的構(gòu)造方法中吗浩,我們找到了線索建芙,registry.append()會(huì)根據(jù)我們load()傳入不同類型的參數(shù),為我們注冊(cè)不同的fetcher懂扼。
// 說明:load傳入?yún)?shù)類型----加載后的資源形式--------加載器----
append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
我們可以這么理解岁钓,我們傳入的參數(shù)是URL,使用HttpGlideUrlLoader來加載微王,最終返回一個(gè)InputStream流屡限。看了注冊(cè)加載器之后炕倘,我們繼續(xù)回到如何構(gòu)建加載器的主路線上:
// LoadData<InputStream>就是加載器包裹钧大,HttpUrlFetcher就是我們要找到真正的網(wǎng)絡(luò)資源加載器!U中0⊙搿!
@Override
public LoadData<InputStream> buildLoadData(
@NonNull GlideUrl model, int width, int height, @NonNull Options options) {
// GlideUrls memoize parsed URLs so caching them saves a few object instantiations and time
// spent parsing urls.
GlideUrl url = model;
if (modelCache != null) {
url = modelCache.get(model, 0, 0);
if (url == null) {
modelCache.put(model, 0, 0, model);
url = model;
}
}
int timeout = options.get(TIMEOUT);
return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
}
此時(shí)就很清晰了涨醋,我們是通過HttpUrlFetcher
來加載資源的瓜饥,然后我們進(jìn)入資源加載環(huán)節(jié)。
4.4 加載資源
從上一小節(jié)中浴骂,我們知道了我們傳入的url是通過HttpUrlFetcher
來加載資源乓土。那么我們就進(jìn)入HttpUrlFetcher
的loadData()。
@Override
public void loadData(@NonNull Priority priority,
@NonNull DataCallback<? super InputStream> callback) {
long startTime = LogTime.getLogTime();
try {
//http 請(qǐng)求溯警,返回一個(gè) InputStream 輸入流
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
//將 InputStream 以回調(diào)形式回調(diào)出去
callback.onDataReady(result);
} catch (IOException e) {
callback.onLoadFailed(e);
} finally {
...
}
}
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl,
Map<String, String> headers) throws IOException {
if (redirects >= MAXIMUM_REDIRECTS) {
throw new HttpException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
} else {
try {
if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
throw new HttpException("In re-direct loop");
}
} catch (URISyntaxException e) {
// Do nothing, this is best effort.
}
}
urlConnection = connectionFactory.build(url);
for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
urlConnection.setConnectTimeout(timeout);
urlConnection.setReadTimeout(timeout);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
urlConnection.setInstanceFollowRedirects(false);
urlConnection.connect();
stream = urlConnection.getInputStream();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (isHttpOk(statusCode)) {
return getStreamForSuccessfulRequest(urlConnection);
}
...//拋的異常我們暫時(shí)先不管
}
從上面代碼我們可以看出 HttpURLConnection 作為 Glide 底層成網(wǎng)絡(luò)請(qǐng)求的趣苏。請(qǐng)求成功之后直接返回的是一個(gè)輸入流,最后會(huì)通過 onDataReady
回調(diào)到 DecodeJob的onDataFetcherReady 函數(shù)中梯轻。
此時(shí)我們已經(jīng)從網(wǎng)絡(luò)上獲取到資源了食磕,現(xiàn)在來看是如何回調(diào)的。
@Override
public void onDataReady(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data;
cb.reschedule();
} else {
cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
loadData.fetcher.getDataSource(), originalKey);
}
}
這里會(huì)有 else 因?yàn)槲覀儧]有配置緩存,繼續(xù)回調(diào):
class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
Runnable,
Comparable<DecodeJob<?>>,
Poolable {
...
@Override
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
DataSource dataSource, Key attemptedKey) {
this.currentSourceKey = sourceKey; //當(dāng)前返回?cái)?shù)據(jù)的 key
this.currentData = data; //返回的數(shù)據(jù)
this.currentFetcher = fetcher; //返回的數(shù)據(jù)執(zhí)行器喳挑,這里可以理解為 HttpUrlFetcher
this.currentDataSource = dataSource; //數(shù)據(jù)來源 url
this.currentAttemptingKey = attemptedKey;
if (Thread.currentThread() != currentThread) {
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
try {
//解析返回回來的數(shù)據(jù)
decodeFromRetrievedData();
} finally {
GlideTrace.endSection();
}
}
}
...
}
至此彬伦,我們的數(shù)據(jù)已經(jīng)從網(wǎng)絡(luò)上加載完成滔悉。最后通過decodeFromRetrievedData()
方法來解析inputstream流。
4.5 解析資源
從上一小節(jié)单绑,我們拿到了資源流回官,下面我們來看看是如何解析資源的。
//解析返回的數(shù)據(jù)
private void decodeFromRetrievedData() {
Resource<R> resource = null;
try {
// 調(diào)用 decodeFrom 解析 數(shù)據(jù)询张;HttpUrlFetcher , InputStream , currentDataSource
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
//解析完成后孙乖,通知下去
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
private <Data> Resource<R> decodeFromData(DataFetcher<?> fetcher, Data data,
DataSource dataSource) throws GlideException {
...
Resource<R> result = decodeFromFetcher(data, dataSource);
....
return result;
} finally {
fetcher.cleanup();
}
}
@SuppressWarnings("unchecked")
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
//獲取當(dāng)前數(shù)據(jù)類的解析器 LoadPath
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
//通過 LoadPath 解析器來解析數(shù)據(jù)
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);
//因?yàn)檫@里返回的是一個(gè) InputStream 所以 這里拿到的是 InputStreamRewinder
DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
try {
//將解析資源的任務(wù)轉(zhuǎn)移到 Load.path 方法中
return path.load(
rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
} finally {
rewinder.cleanup();
}
}
注意上面代碼浙炼,為了解析數(shù)據(jù)首先構(gòu)建一個(gè) LoadPath, 然后創(chuàng)建一個(gè) InputStreamRewinder 類型的 DataRewinder, 最終將數(shù)據(jù)解析的操作放到了 LoadPath.load 方法中 份氧,接下來看下 LoadPath.load 方法的具體邏輯操作:
public Resource<Transcode> load(DataRewinder<Data> rewinder, @NonNull Options options, int width,
int height, DecodePath.DecodeCallback<ResourceType> decodeCallback) throws GlideException {
try {
return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);
} finally {
listPool.release(throwables);
}
}
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;
//遍歷內(nèi)部存儲(chǔ)的 DecodePath 集合,通過他們來解析數(shù)據(jù)
for (int i = 0, size = decodePaths.size(); i < size; i++) {
DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
try {
//這里才是真正解析數(shù)據(jù)的地方
result = path.decode(rewinder, width, height, options, decodeCallback);
} catch (GlideException e) {
...
}
...
return result;
}
最終通過path.decode()根據(jù)尺寸來解析inputstream流弯屈。
public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
@NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
//調(diào)用 decodeResourec 將數(shù)據(jù)解析成中間資源
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
//解析完數(shù)據(jù)回調(diào)出去
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
//轉(zhuǎn)換資源為目標(biāo)資源
return transcoder.transcode(transformed, options);
}
看看 decodeResource 怎么解析成中間資源的:
@NonNull
private Resource<ResourceType> decodeResource(DataRewinder<DataType> rewinder, int width,
int height, @NonNull Options options) throws GlideException {
...
try {
return decodeResourceWithList(rewinder, width, height, options, exceptions);
} finally {
...
}
}
@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();
// 調(diào)用 ResourceDecoder.decode 解析數(shù)據(jù)
result = decoder.decode(data, width, height, options);
}
} catch (IOException | RuntimeException | OutOfMemoryError e) {
...
}
return result;
}
可以看到數(shù)據(jù)解析的任務(wù)最終是通過 DecodePath 來執(zhí)行的, 它內(nèi)部有三大步操作
- 第一大步:deResource 將源數(shù)據(jù)解析成資源(源數(shù)據(jù): InputStream蜗帜, 中間產(chǎn)物: Bitmap)
- 第二大步:調(diào)用 DecodeCallback.onResourceDecoded 處理資源
- 第三大步:調(diào)用 ResourceTranscoder.transcode 將資源轉(zhuǎn)為目標(biāo)資源(目標(biāo)資源類型: Drawable)
可以發(fā)現(xiàn),通過上面的 decoder.decode 源碼可知资厉,它是一個(gè)接口厅缺,由于我們這里的源數(shù)據(jù)是 InputStream,所以,它的實(shí)現(xiàn)類是 StreamBitmapDecoder類 ,我們就來看下 它內(nèi)部的解碼過程:
@Override
public Resource<Bitmap> decode(@NonNull InputStream source, int width, int height,
@NonNull Options options)
throws IOException {
// Use to fix the mark limit to avoid allocating buffers that fit entire images.
final RecyclableBufferedInputStream bufferedStream;
final boolean ownsBufferedStream;
....
try {
// 根據(jù)請(qǐng)求配置來對(duì)數(shù)據(jù)進(jìn)行采樣壓縮宴偿,獲取一個(gè) Resource<Bitmap>
return downsampler.decode(invalidatingStream, width, height, options, callbacks);
} finally {
....
}
}
最終是通過downsampler去根據(jù)目標(biāo)的寬高湘捎,來對(duì)輸入數(shù)據(jù)流進(jìn)行壓縮。
回到主線中的三大步:
public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
@NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
//第一步: 調(diào)用 decodeResourec 將數(shù)據(jù)解析成中間資源 Bitmap
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
//第二步: 解析完數(shù)據(jù)回調(diào)出去
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
//第三步: 轉(zhuǎn)換資源為目標(biāo)資源 Bitmap to Drawable
return transcoder.transcode(transformed, options);
}
解析完數(shù)據(jù)窄刘,看第二注釋里面回調(diào)窥妇,最后會(huì)回調(diào)到 DecodeJob:
class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
Runnable,
Comparable<DecodeJob<?>>,
Poolable {
...
@Override
public Resource<Z> onResourceDecoded(@NonNull Resource<Z> decoded) {
return DecodeJob.this.onResourceDecoded(dataSource, decoded);
}
...
}
@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;
//如果不是從磁盤資源中獲取需要進(jìn)行 transform 操作
if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
transformed = appliedTransformation.transform(glideContext, decoded, width, height);
}
...
//構(gòu)建數(shù)據(jù)編碼的策略
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;
}
//根據(jù)編碼策略,構(gòu)建緩存 Key
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:
//源數(shù)據(jù) key
key = new DataCacheKey(currentSourceKey, signature);
break;
//... 省略 成噸的代碼
}
//初始化編碼管理者娩践,用于提交內(nèi)存緩存
LockedResource<Z> lockedResult = LockedResource.obtain(transformed);
deferredEncodeManager.init(key, encoder, lockedResult);
result = lockedResult;
}
//返回轉(zhuǎn)換后的 Bitmap
return result;
}
此處說白了活翩, onResourceDecoded 中, 主要是對(duì)中間資源做了如下的操作:
第一步:對(duì)資源進(jìn)行了轉(zhuǎn)換操作。比如 Fit_Center,CenterCrop, 這些都是在請(qǐng)求的時(shí)候配置的翻伺;
第二步:構(gòu)建磁盤緩存的 key材泄。
資源解析完成,已經(jīng)存入磁盤吨岭,此處開始對(duì)資源繼續(xù)轉(zhuǎn)換拉宗。
4.6 資源轉(zhuǎn)換
從上一節(jié)我們通過解碼inputstream流得到Bitmap,然而我們顯示需要將Bitmap轉(zhuǎn)化成Drawable辣辫。下面我們繼續(xù)接著上一小節(jié)看看資源是如何轉(zhuǎn)換的簿废。
public class DecodePath<DataType, ResourceType, Transcode> {
//省略成噸的代碼 ...
Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
@NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
//第一步: 調(diào)用 decodeResourec 將數(shù)據(jù)解析成中間資源 Bitmap
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
//第二步: 解析完數(shù)據(jù)回調(diào)出去
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
//第三步: 轉(zhuǎn)換資源為目標(biāo)資源 Bitmap to Drawable
return transcoder.transcode(transformed, options);
}
// 省略成噸的代碼 ...
}
我們繼續(xù)看第三步。通過源碼可知络它,ResourceTranscoder 是一個(gè)接口族檬,又因?yàn)榻馕鐾甑臄?shù)據(jù)是 Bitmap 所以它的實(shí)現(xiàn)類是 BitmapDrawableTranscoder ,最后看下它的 transcode 具體實(shí)現(xiàn):
public class BitmapDrawableTranscoder implements ResourceTranscoder<Bitmap, BitmapDrawable> {
@Nullable
@Override
public Resource<BitmapDrawable> transcode(@NonNull Resource<Bitmap> toTranscode,
@NonNull Options options) {
return LazyBitmapDrawableResource.obtain(resources, toTranscode);
}
}
public final class LazyBitmapDrawableResource implements Resource<BitmapDrawable>,
Initializable {
private final Resources resources;
private final Resource<Bitmap> bitmapResource;
@Deprecated
public static LazyBitmapDrawableResource obtain(Context context, Bitmap bitmap) {
return
(LazyBitmapDrawableResource)
obtain(
context.getResources(),
BitmapResource.obtain(bitmap, Glide.get(context).getBitmapPool()));
}
@Deprecated
public static LazyBitmapDrawableResource obtain(Resources resources, BitmapPool bitmapPool,Bitmap bitmap) {
return (LazyBitmapDrawableResource) obtain(resources, BitmapResource.obtain(bitmap, bitmapPool));
}
@Nullable
public static Resource<BitmapDrawable> obtain(
@NonNull Resources resources, @Nullable Resource<Bitmap> bitmapResource) {
if (bitmapResource == null) {
return null;
}
return new LazyBitmapDrawableResource(resources, bitmapResource);
}
private LazyBitmapDrawableResource(@NonNull Resources resources,
@NonNull Resource<Bitmap> bitmapResource) {
this.resources = Preconditions.checkNotNull(resources);
this.bitmapResource = Preconditions.checkNotNull(bitmapResource);
}
@NonNull
@Override
public Class<BitmapDrawable> getResourceClass() {
return BitmapDrawable.class;
}
// Get 方法返回了一個(gè) BitmapDrawable 對(duì)象
@NonNull
@Override
public BitmapDrawable get() {
return new BitmapDrawable(resources, bitmapResource.get());
}
@Override
public int getSize() {
return bitmapResource.getSize();
}
@Override
public void recycle() {
bitmapResource.recycle();
}
@Override
public void initialize() {
if (bitmapResource instanceof Initializable) {
((Initializable) bitmapResource).initialize();
}
}
}
轉(zhuǎn)化終于完成了 化戳,將我們解析到的 bitmap 存放到 LazyBitmapDrawableResource 內(nèi)部, 然后外界通過 get 方法就可以獲取到一個(gè) BitmapDrawable 的對(duì)象了单料,解析完就到了展示數(shù)據(jù)了埋凯,請(qǐng)看下面代碼:
class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
Runnable,
Comparable<DecodeJob<?>>,
Poolable {
//解析返回的數(shù)據(jù)
private void decodeFromRetrievedData() {
Resource<R> resource = null;
try {
//第一步: 調(diào)用 decodeFrom 解析 數(shù)據(jù);HttpUrlFetcher , InputStream , currentDataSource
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
//第二步: 解析完成后扫尖,通知下去
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
第一步就解析完了數(shù)據(jù)白对, 現(xiàn)在第二步執(zhí)行 notifyEncodeAndRelease函數(shù):
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
...
//通知調(diào)用層數(shù)據(jù)已經(jīng)裝備好了
notifyComplete(result, dataSource);
stage = Stage.ENCODE;
try {
//這里就是將資源磁盤緩存
if (deferredEncodeManager.hasResourceToEncode()) {
deferredEncodeManager.encode(diskCacheProvider, options);
}
} finally {
...
}
//完成
onEncodeComplete();
}
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
setNotifiedOrThrow();
// 在 DecodeJob 的構(gòu)建中, 我們知道這個(gè) Callback 是 EngineJob
callback.onResourceReady(resource, dataSource);
}
}
可以看到上面的 DecodeJob.decodeFromRetrievedData 中主要做了三個(gè)處理:
第一個(gè)處理:解析返回回來的資源。
第二個(gè)處理:拿到解析的資源换怖,如果配置了本地緩存甩恼,就緩存到磁盤。
第三個(gè)處理:通知上層資源準(zhǔn)備就緒沉颂,可以使用了条摸。
我們直接看 EngineJob 的 onResourceReady 回調(diào)函數(shù):
@Override
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
synchronized (this) {
this.resource = resource;
this.dataSource = dataSource;
}
notifyCallbacksOfResult();
}
@Synthetic
void notifyCallbacksOfResult() {
ResourceCallbacksAndExecutors copy;
Key localKey;
EngineResource<?> localResource;
synchronized (this) {
stateVerifier.throwIfRecycled();
if (isCancelled) {
resource.recycle();
release();
return;
} else if (cbs.isEmpty()) {
...
}
engineResource = engineResourceFactory.build(resource, isCacheable);
hasResource = true;
copy = cbs.copy();
incrementPendingCallbacks(copy.size() + 1);
localKey = key;
localResource = engineResource;
}
//回調(diào)上層 Engine 任務(wù)完成了
listener.onEngineJobComplete(this, localKey, localResource);
//遍歷資源回調(diào)給 ImageViewTarget
for (final ResourceCallbackAndExecutor entry : copy) {
entry.executor.execute(new CallResourceReady(entry.cb));
}
decrementPendingCallbacks();
}
通過上面 EngineJob 的 onResourceReady 回調(diào)函數(shù) 主要做了 兩個(gè)處理:
第一個(gè)處理:通知上層任務(wù)完成。
第二個(gè)處理:回調(diào) ImageViewTarget 用于展示數(shù)據(jù)铸屉。
看下 listener.onEngineJobComplete 具體實(shí)現(xiàn):
@SuppressWarnings("unchecked")
@Override
public synchronized void onEngineJobComplete(
EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
if (resource != null) {
resource.setResourceListener(key, this);
//收到下游返回回來的資源钉蒲,添加到活動(dòng)緩存中
if (resource.isCacheable()) {
activeResources.activate(key, resource);
}
}
jobs.removeIfCurrent(key, engineJob);
}
最終通知 ImageViewTarget, 看下具體操作:
//遍歷資源回調(diào)給 ImageViewTarget
for (final ResourceCallbackAndExecutor entry : copy) {
entry.executor.execute(new CallResourceReady(entry.cb));
}
private class CallResourceReady implements Runnable {
private final ResourceCallback cb;
CallResourceReady(ResourceCallback cb) {
this.cb = cb;
}
@Override
public void run() {
synchronized (EngineJob.this) {
if (cbs.contains(cb)) {
...
//返回準(zhǔn)備好的資源
callCallbackOnResourceReady(cb);
removeCallback(cb);
}
decrementPendingCallbacks();
}
}
}
我們可以看到 CallResourceReady 實(shí)現(xiàn) Runnable ,當(dāng) entry.executor.execute 線程池執(zhí)行的時(shí)候就會(huì)調(diào)用 run 彻坛,最后我們繼續(xù)跟 callCallbackOnResourceReady函數(shù):
@Synthetic
synchronized void callCallbackOnResourceReady(ResourceCallback cb) {
try {
//回調(diào)給 SingleRequest
cb.onResourceReady(engineResource, dataSource);
} catch (Throwable t) {
throw new CallbackException(t);
}
}
SingleRequest onResourceReady 回調(diào)實(shí)現(xiàn):
public synchronized void onResourceReady(Resource<?> resource, DataSource dataSource) {
stateVerifier.throwIfRecycled();
loadStatus = null;
... 省略成噸的代碼
Object received = resource.get();
if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
releaseResource(resource);
... 省略成噸的代碼
onLoadFailed(exception);
return;
}
if (!canSetResource()) {
releaseResource(resource);
status = Status.COMPLETE;
return;
}
//當(dāng)資源準(zhǔn)備好的時(shí)候
onResourceReady((Resource<R>) resource, (R) received, dataSource);
}
private synchronized void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
... 省略成噸的代碼
anyListenerHandledUpdatingTarget |=
targetListener != null
&& targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
if (!anyListenerHandledUpdatingTarget) {
Transition<? super R> animation =
animationFactory.build(dataSource, isFirstResource);
//回調(diào)給目標(biāo) ImageViewTarget 資源準(zhǔn)備好了
target.onResourceReady(result, animation);
}
} finally {
isCallingCallbacks = false;
}
//加載成功
notifyLoadSuccess();
}
這一步主要把準(zhǔn)備好的資源回調(diào)給顯示層!!!!!!! 終于快看到勝利的曙光了顷啼。
4.7 顯示資源
資源終于完全準(zhǔn)備好了,下面就是加載資源到控件上昌屉。
public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z>
implements Transition.ViewAdapter {
...
@Override
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
if (transition == null || !transition.transition(resource, this)) {
setResourceInternal(resource);
} else {
maybeUpdateAnimatable(resource);
}
}
protected abstract void setResource(@Nullable Z resource);
...
}
private void setResourceInternal(@Nullable Z resource) {
//調(diào)用 setResource 函數(shù)钙蒙,將資源顯示出來
setResource(resource);
...
}
大家還記得么?在最開始構(gòu)建的時(shí)候间驮,我們知道只有調(diào)用 asBitmap 的時(shí)候?qū)崿F(xiàn)類是 BitmapImageViewTarget
,在這里的測(cè)試躬厌,并沒有調(diào)用這個(gè)函數(shù),所以它的實(shí)現(xiàn)類是 DrawableImageViewTarget
,具體看下它內(nèi)部實(shí)現(xiàn):
public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {
public DrawableImageViewTarget(ImageView view) {
super(view);
}
// Public API.
@SuppressWarnings({"unused", "deprecation"})
@Deprecated
public DrawableImageViewTarget(ImageView view, boolean waitForLayout) {
super(view, waitForLayout);
}
@Override
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
}
這里看到抽象類中調(diào)用了 setResource ,子類實(shí)現(xiàn)并調(diào)用了 view.setImageDrawable(resource); 圖片現(xiàn)在算是真正的顯示出來了蜻牢。