注意:如果發(fā)現(xiàn)Glide解析系列的連接訪(fǎng)問(wèn)不了性置,表示此文章是規(guī)劃尚未完成的文章
背景
Glide的使用不在本系列的說(shuō)明范圍,如果要想知道其使用方式請(qǐng)到Glide官網(wǎng)查看混埠。
Gilde是一款快速高效的圖片加載庫(kù)怠缸。Glide老早就出來(lái)了,且其應(yīng)用范圍非常廣钳宪,為什么它深受各位開(kāi)發(fā)者的青睞揭北?它有什么過(guò)人之處?它的框架設(shè)計(jì)的好還是壞吏颖?作為一名渴望進(jìn)步的開(kāi)發(fā)者來(lái)說(shuō)是有必要進(jìn)行深入了解的搔体。如果只關(guān)注它的使用方式,不了解它的思想原理半醉、精髓疚俱,我們只能停留在搬運(yùn)工的階層,很難達(dá)到牛人的階層缩多。
我接下來(lái)將對(duì)Glide進(jìn)行各個(gè)維度的原理解析呆奕。本篇文章先從Glide的整體流程說(shuō)起。
1衬吆、入口
一般情況下我們都是通過(guò)如下的方式使用Glide進(jìn)行加載圖片的:
GlideApp.with(activity)
.load(myUrl)
.placeholder(placeholder)
.fitCenter()
.into(imageView);
GlideApp是通過(guò)APT根據(jù)我們自定義的GlideModule動(dòng)態(tài)生成的梁钾,其with的實(shí)現(xiàn)如下:
@NonNull
public static GlideRequests with(@NonNull Activity activity) {
return (GlideRequests) Glide.with(activity);
}
GlideApp內(nèi)部調(diào)用Glide的with方法,大致看一下GlideApp的所有代碼逊抡,其內(nèi)部都是調(diào)用Glide的方法實(shí)現(xiàn)的姆泻,為什么不直接使用Glide而要通過(guò)GlideApp調(diào)用呢?其實(shí)GlideApp可以理解為是Glide的一個(gè)包裹類(lèi)秦忿,這樣的目的是為了封裝麦射、安全性蛾娶。
2灯谣、Glide的with實(shí)現(xiàn)
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
Glidewith方法,返回的是RequestManager蛔琅,而RequestManager是通過(guò)getRetriever().get獲取的胎许,我們看下getRetriever:
2.1、Glide的getRetriever:
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
//省略...
return Glide.get(context).getRequestManagerRetriever();
}
getRetriever返回的是RequestManagerRetriever罗售,它先獲取Glide的單例(Glide的初始化通過(guò)Glide.get實(shí)現(xiàn)辜窑,Glide的初始化解析請(qǐng)看這里Glide解析二:Glide的初始化),再使用Glide單例中的getRequestManagerRetriever返回RequestManagerRetriever寨躁,getRequestManagerRetriever用來(lái)干嘛的穆碎?請(qǐng)查看Glide解析三:Glide是如何感知組件生命周期的
2.1.1、RequestManagerRetriever的get返回RequestManager
public RequestManager get(@NonNull Activity activity) {
if (Util.isOnBackgroundThread()) {
//如果是在子線(xiàn)程职恳,則綁定Application的生命周期
return get(activity.getApplicationContext());
} else {
//如果是在UI線(xiàn)程所禀,則綁定Activity的生命周期
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(
activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
RequestManagerRetriever有幾個(gè)get方面,參數(shù)分別有context、fragment色徘、activity恭金、view,其思想主要是根據(jù)不同的生命周期對(duì)象創(chuàng)建一個(gè)RequestManagerFragment褂策,根據(jù)這個(gè)RequestManagerFragment實(shí)現(xiàn)自動(dòng)感知各大組件的生命周期實(shí)現(xiàn)横腿。接著創(chuàng)建RequestManager并綁定RequestManagerFragment,RequestManager主要用來(lái)管理和發(fā)起一個(gè)Glide加載圖片的請(qǐng)求斤寂,因?yàn)榻壎薘equestManagerFragment所以可以感知各大組件的生命周期耿焊,并根據(jù)生命周期對(duì)Glide加載圖片進(jìn)行restart、或者stop等操作扬蕊。
具體的請(qǐng)查看Glide解析三:Glide是如何感知組件生命周期的
3搀别、RequestManager的load實(shí)現(xiàn)
步驟2中解析到Glide的get返回RequestManager,然后使用ReuqestManager進(jìn)行l(wèi)oad操作尾抑,我們看下load的源碼:
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
load方法先通過(guò)asDrawable方法創(chuàng)建一個(gè)RequestBuilder歇父,在調(diào)用RequestBuilder的load。as系列操作主要是用于標(biāo)識(shí)將圖片解析成Bitmap還是Drawable再愈。
4榜苫、RequestBuilder
步驟3中解析了RequestManager的load方法先創(chuàng)建一個(gè)RequestBuilder,再調(diào)用RequestBuilder的系列方法設(shè)置一些配置信息想transform翎冲、apply垂睬、error、into等抗悍,接著RequestBuilder再構(gòu)建Glide圖片加載對(duì)象Request驹饺,通過(guò)Request對(duì)象實(shí)現(xiàn)圖片加載的請(qǐng)求。
4.1缴渊、RequestBuilder的load方法
public RequestBuilder<TranscodeType> load(@Nullable String string) {
return loadGeneric(string);
}
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
}
load方法有多重赏壹,可以傳入String、Bitmap衔沼、Uri蝌借、Drawable、file等指蚁,作為model菩佑,代表圖片加載來(lái)源的數(shù)據(jù)模型。String凝化、uri一般是代表從圖片來(lái)源于網(wǎng)絡(luò)稍坯;Bitmap代表圖片來(lái)源當(dāng)前傳進(jìn)來(lái)的Bitmap;Drawable代表圖片來(lái)源于當(dāng)前傳進(jìn)來(lái)的Drawable搓劫;file代表圖片來(lái)源文件瞧哟。
4.2袜蚕、RequestBuilder的transition方法
public RequestBuilder<TranscodeType> transition(
@NonNull TransitionOptions<?, ? super TranscodeType> transitionOptions) {
this.transitionOptions = Preconditions.checkNotNull(transitionOptions);
isDefaultTransitionOptionsSet = false;
return this;
}
transition方法只是設(shè)置配置glide圖片加載之后圖片轉(zhuǎn)換器,并標(biāo)識(shí)為不適用默認(rèn)的轉(zhuǎn)換器
4.3绢涡、RequestBuilder的apply方法
public RequestBuilder<TranscodeType> apply(@NonNull RequestOptions requestOptions) {
Preconditions.checkNotNull(requestOptions);
this.requestOptions = getMutableOptions().apply(requestOptions);
return this;
}
apply方法配置Glide請(qǐng)求的一些配置選項(xiàng)信息
4.4牲剃、RequestBuilder的error方法
public RequestBuilder<TranscodeType> error(@Nullable RequestBuilder<TranscodeType> errorBuilder) {
this.errorBuilder = errorBuilder;
return this;
}
error方法配置圖片加載失敗時(shí)的處理器
4.5、RequestBuilder的listener方法
public RequestBuilder<TranscodeType> listener(
@Nullable RequestListener<TranscodeType> requestListener) {
this.requestListeners = null;
return addListener(requestListener);
}
public RequestBuilder<TranscodeType> addListener(
@Nullable RequestListener<TranscodeType> requestListener) {
if (requestListener != null) {
if (this.requestListeners == null) {
this.requestListeners = new ArrayList<>();
}
this.requestListeners.add(requestListener);
}
return this;
}
listener內(nèi)部調(diào)用addListener方法雄可,添加圖片加載請(qǐng)求狀態(tài)和結(jié)果監(jiān)聽(tīng)器
4.6凿傅、RequestBuilder的into方法
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
//檢測(cè)是否在UI線(xiàn)程,如果不是在UI線(xiàn)程則拋出異常
Util.assertMainThread();
//如果view為空則拋出隱藏
Preconditions.checkNotNull(view);
//根據(jù)ImageView的縮放類(lèi)型設(shè)置對(duì)應(yīng)的glide圖片縮放處理器
RequestOptions requestOptions = this.requestOptions;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
// Clone in this method so that if we use this RequestBuilder to load into a View and then
// into a different target, we don't retain the transformation applied based on the previous
// View's scale type.
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions = requestOptions.clone().optionalFitCenter();
break;
case FIT_XY:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case CENTER:
case MATRIX:
default:
// Do nothing.
}
}
//先構(gòu)建ViewTarget数苫,調(diào)用into的重載方法
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions);
}
into傳遞的是ImageView時(shí)聪舒,先構(gòu)建為ViewTarget,再調(diào)用into的另一個(gè)重載方法虐急,我們看下ViewTarget的構(gòu)建實(shí)現(xiàn):
//Glide.java
public <X> ViewTarget<ImageView, X> buildImageViewTarget(
@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
//ImageViewTargetFactory.java
public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view,
@NonNull Class<Z> clazz) {
if (Bitmap.class.equals(clazz)) {
return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
} 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)");
}
}
這里ImageViewTargetFactory根據(jù)加載圖片加載回來(lái)的類(lèi)型箱残,構(gòu)建BitmapImageViewTarget或者DrawableImageViewTarget,默認(rèn)是DrawableImageViewTarget止吁。兩者均繼承成ImageViewTarget被辑,ImageViewTarget繼承自ViewTarget,ViewTarget實(shí)現(xiàn)Target接口敬惦。ViewTarget主要用來(lái)根據(jù)圖片加載的狀態(tài)和結(jié)果顯示對(duì)于的圖片盼理、動(dòng)畫(huà),根據(jù)對(duì)應(yīng)的生命周期對(duì)圖片加載請(qǐng)求進(jìn)行暫停俄删、停止宏怔、重新加載等邏輯。Glide解析四:為什么用Target而不直接用ImageView
我們接著看into的重載方法:
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
@NonNull RequestOptions options) {
//校驗(yàn)是否在Ui線(xiàn)程畴椰,如果不是則拋出異常
Util.assertMainThread();
//校驗(yàn)target是否為空臊诊,如果未空則拋出異常
Preconditions.checkNotNull(target);
//如果沒(méi)有設(shè)置圖片加載來(lái)源,即從哪里加載圖片斜脂,則拋出異常
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
//克隆圖片加載配置選項(xiàng)
options = options.autoClone();
//構(gòu)建圖片加載請(qǐng)求對(duì)象
Request request = buildRequest(target, targetListener, options);
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
//如果之前已經(jīng)存在圖片加載請(qǐng)求對(duì)象抓艳,并且不是請(qǐng)求狀態(tài),則發(fā)起請(qǐng)求
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;
}
//取消正在等待加載或者回收已經(jīng)加載的資源
requestManager.clear(target);
//target設(shè)置圖片加載請(qǐng)求對(duì)象
target.setRequest(request);
//發(fā)起請(qǐng)求
requestManager.track(target, request);
return target;
}
這里構(gòu)建的是request對(duì)象秽褒,如果我們使用時(shí)不指明Thumbnail壶硅,那么構(gòu)建的默認(rèn)是SingleRequest威兜。
我們看下requestManager.track是如何發(fā)起請(qǐng)求的:
void track(@NonNull Target<?> target, @NonNull Request request) {
//添加target到追蹤列表中
targetTracker.track(target);
//通過(guò)runRequest發(fā)起請(qǐng)求
requestTracker.runRequest(request);
}
public void runRequest(@NonNull Request request) {
requests.add(request);
if (!isPaused) {
//當(dāng)前狀態(tài)不是暫停销斟,則開(kāi)始發(fā)起圖片加載請(qǐng)求
request.begin();
} else {
//如果處于暫停狀態(tài),則取消當(dāng)前圖片加載請(qǐng)求
request.clear();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Paused, delaying request");
}
//加入等待加載列表
pendingRequests.add(request);
}
}
5椒舵、SingleRequest
SingleRequest是Reuqest的實(shí)現(xiàn)類(lèi)蚂踊,其主要用實(shí)現(xiàn)圖片加載的邏輯。
步驟4中分析到圖片加載的開(kāi)始是在request的begin方法笔宿,我們看下
5.1犁钟、SingleRequest的begin實(shí)現(xiàn):
public void begin() {
//如果正在調(diào)用回調(diào)方法棱诱,則調(diào)用此方法就會(huì)拋出異常
assertNotCallingCallbacks();
//如果已經(jīng)被回收則拋出異常
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
if (model == null) {
//如果圖片加載來(lái)源,即從哪里加載涝动,則回調(diào)失敗
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;
}
//如果當(dāng)前狀態(tài)正處于加載圖片的狀態(tài)迈勋,則拋出異常
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) {
//如果當(dāng)前狀態(tài)處于已經(jīng)加載完成,則回調(diào)資源已經(jīng)加載完畢
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.
//標(biāo)識(shí)當(dāng)前狀態(tài)為等待獲取View的大小信息
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
//如果View大小已經(jīng)就緒醋粟,則調(diào)用onSizeReady方法進(jìn)行圖片加載請(qǐng)求
onSizeReady(overrideWidth, overrideHeight);
} else {
//如果view大小未就緒靡菇,則先去獲取view的大小
target.getSize(this);
}
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
//通知圖片開(kāi)始加載
target.onLoadStarted(getPlaceholderDrawable());
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
begin的步驟可以歸納如下:
a、如果當(dāng)前狀態(tài)是正在加載圖片米愿,則拋出異常厦凤,結(jié)束
b、如果當(dāng)前狀態(tài)是已經(jīng)加載完成育苟,則回調(diào)加載圖片已就緒方法
c较鼓、如果當(dāng)前View大小已經(jīng)就緒,則調(diào)用onSizeReady方法實(shí)現(xiàn)圖片加載
d违柏、如果當(dāng)前View大小為就緒博烂,則等待View大小就緒再回調(diào)onSizeReady實(shí)現(xiàn)圖片加載
e、調(diào)用onLoadStarted通知圖片開(kāi)始加載
至于當(dāng)View的大小在未知的情況下漱竖,怎么獲取view的大小并回調(diào)onSizeReady的脖母,請(qǐng)查看Glide解析四:為什么用Target而不直接用ImageView
5.2、SingleRequest的onSizeReady實(shí)現(xiàn):
public 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) {
//如果當(dāng)前狀態(tài)不處于等待獲取View大小闲孤,則返回結(jié)束
return;
}
//更改當(dāng)前狀態(tài)為正在運(yùn)行圖片加載中
status = Status.RUNNING;
//獲取圖片大小的乘積系數(shù)
float sizeMultiplier = requestOptions.getSizeMultiplier();
//如果圖片大小為-1谆级,則返回圖片大小為負(fù)數(shù)芬探,否則=大小*sizeMultiplier
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
if (IS_VERBOSE_LOGGABLE) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
//通過(guò)圖片加載引擎加載圖片
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);
// 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的核心是調(diào)用圖片加載引擎engine的load方法加載圖片
6雁芙、Engine
6.1、Engine.load的實(shí)現(xiàn):
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) {
Util.assertMainThread();
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
//構(gòu)建key
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
//根據(jù)key從活躍的資源列表中獲取圖片資源對(duì)象
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
//如果活躍的圖片資源對(duì)象存在歹河,則回調(diào)資源加載已就緒方法
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
//根據(jù)key從緩存中加載圖片資源對(duì)象
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
//如果緩存圖片資源對(duì)象存在勤众,則回調(diào)資源加載已就緒方法
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
//根據(jù)key從圖片加載任務(wù)中獲取任務(wù)對(duì)象
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
//如果已經(jīng)有任務(wù)正在加載圖片資源舆绎,則添加回調(diào),等待任務(wù)完成之后調(diào)度
current.addCallback(cb);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
//構(gòu)建一個(gè)新的圖片加載任務(wù)對(duì)象
EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
//構(gòu)建一個(gè)新的圖片解析任務(wù)對(duì)象
DecodeJob<R> decodeJob =
decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);
//將新構(gòu)建的圖片加載任務(wù)加入任務(wù)列表中
jobs.put(key, engineJob);
//添加回調(diào)
engineJob.addCallback(cb);
//調(diào)用圖片加載任務(wù)的start方法開(kāi)始加載圖片
engineJob.start(decodeJob);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
對(duì)于圖片加載引擎Engine的load實(shí)現(xiàn)可以歸納如下:
a们颜、構(gòu)建圖片加載對(duì)應(yīng)的key
b吕朵、如果活躍資源列表中存在要加載的圖片資源,則回調(diào)資源準(zhǔn)備就緒
c窥突、如果緩存列表中存在要加載的圖片資源努溃,則回調(diào)資源準(zhǔn)備就緒
d、如果存在要加載圖片對(duì)于的圖片加載任務(wù)阻问,則添加回調(diào)給圖片加載任務(wù)梧税,等待任務(wù)加載圖片完成調(diào)度
e、構(gòu)建圖片加載任務(wù)和圖片解析任務(wù)對(duì)象
f、通過(guò)圖片加載任務(wù)加載任務(wù)
不知道對(duì)于上面的的這幾個(gè)步驟你是否有疑問(wèn)第队,反正我是有的哮塞,不是說(shuō)三層緩存策略嗎?一是緩存凳谦,二是磁盤(pán)忆畅,三是網(wǎng)絡(luò),這里看上去像只有一和三尸执,二的磁盤(pán)緩存呢邻眷?難道是在圖片加載任務(wù)EngineJob或者DecodeJob中?是的剔交,待下文詳解肆饶。
這里為什么還要有個(gè)活躍緩存?請(qǐng)查看Glide解析五:活躍緩存策略
對(duì)于緩存策略請(qǐng)看這里Glide解析六:緩存策略
7岖常、EngineJob
7.1驯镊、EngineJob的start
public void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor = decodeJob.willDecodeFromCache()
? diskCacheExecutor
: getActiveSourceExecutor();
executor.execute(decodeJob);
}
boolean willDecodeFromCache() {
//獲取下一個(gè)狀態(tài)
Stage firstStage = getNextStage(Stage.INITIALIZE);
//如果當(dāng)前狀態(tài)為RESOURCE_CACHE 或者DATA_CACHE代表從磁盤(pán)緩存中讀取
return firstStage == Stage.RESOURCE_CACHE || firstStage == Stage.DATA_CACHE;
}
//根據(jù)當(dāng)前狀態(tài)獲取下一個(gè)狀態(tài)
private Stage getNextStage(Stage current) {
switch (current) {
case INITIALIZE:
//當(dāng)前狀態(tài)為初始狀態(tài)——》如果允許從已緩存的資源中讀取,返回轉(zhuǎn)換后磁盤(pán)緩存狀態(tài)竭鞍,否則返回下一個(gè)狀態(tài)
return diskCacheStrategy.decodeCachedResource()
? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
case RESOURCE_CACHE:
//當(dāng)前狀態(tài)從轉(zhuǎn)換后磁盤(pán)緩存讀取——》如果允許從已緩存原始圖片磁盤(pán)緩存中讀取板惑,則返回原始磁盤(pán)緩存狀態(tài),否則返回下一個(gè)狀態(tài)
return diskCacheStrategy.decodeCachedData()
? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
case DATA_CACHE:
//當(dāng)前已緩存原始圖片磁盤(pán)緩存中讀取偎快,如果只從緩存中讀取冯乘,則返回完成狀態(tài),否則返回從網(wǎng)絡(luò)讀取狀態(tài)
// Skip loading from source if the user opted to only retrieve the resource from cache.
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
//當(dāng)前狀態(tài)從網(wǎng)絡(luò)讀取或者已完成狀態(tài)晒夹,則返回已完成狀態(tài)
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}
這段代碼可以看到如果允許從磁盤(pán)緩存中讀取裆馒,則先從磁盤(pán)緩存中讀取,接著是從網(wǎng)絡(luò)讀取丐怯。
上面的代碼使用GlideExecutor 線(xiàn)程池調(diào)度decodeJob線(xiàn)程喷好,使用decodeJob實(shí)現(xiàn)從磁盤(pán)、數(shù)據(jù)源读跷、網(wǎng)絡(luò)中加載圖片梗搅。這里說(shuō)明下RESOURCE_CACHE、DATA_CACHE效览、SOURCE這三種狀態(tài)的明確意思:
- RESOURCE_CACHE: 從磁盤(pán)緩存中讀取轉(zhuǎn)換之后的圖片无切,而這里的圖片指的是原始圖片轉(zhuǎn)換之后的圖片,比如從網(wǎng)絡(luò)下載下來(lái)之后丐枉,先緩存到磁盤(pán)的就是原始的圖片哆键,經(jīng)過(guò)對(duì)原始圖片進(jìn)行大小、方向等轉(zhuǎn)換之后的圖片就是轉(zhuǎn)換之后的圖片矛洞。
- DATA_CACHE: 從磁盤(pán)緩存中讀取原始圖片洼哎,而這個(gè)磁盤(pán)緩存存的就是原始圖片
- SOURCE: 代表從網(wǎng)絡(luò)上加載的狀態(tài)
7、DecodeJob
DecodeJob是一個(gè)線(xiàn)程對(duì)象沼本,其實(shí)現(xiàn)了Runnable接口:
class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
Runnable,
Comparable<DecodeJob<?>>,
Poolable {
//...
}
7.1噩峦、DecodeJob的run
@Override
public void run() {
//...
DataFetcher<?> localFetcher = currentFetcher;
try {
if (isCancelled) {
//已取消,則通知加載失敗
notifyFailed();
return;
}
//run的包裹實(shí)現(xiàn)
runWrapped();
} catch (Throwable t) {
//...
if (stage != Stage.ENCODE) {
//當(dāng)前狀態(tài)不等于解析圖片狀態(tài)抽兆,則代表加載失敗
throwables.add(t);
//通知加載失敗
notifyFailed();
}
if (!isCancelled) {
throw t;
}
} finally {
if (localFetcher != null) {
//清除數(shù)據(jù)识补、釋放相關(guān)資源
localFetcher.cleanup();
}
GlideTrace.endSection();
}
}
run接著調(diào)用runWrapped方法
7.2、DecodeJob的runWrapped
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
//當(dāng)前狀態(tài)是初始化狀態(tài)
//根據(jù)當(dāng)前狀態(tài)獲取新的狀態(tài)
stage = getNextStage(Stage.INITIALIZE);
//獲取當(dāng)前狀態(tài)的執(zhí)行器
currentGenerator = getNextGenerator();
//運(yùn)行當(dāng)前圖片加載執(zhí)行器
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
//當(dāng)前狀態(tài)轉(zhuǎn)為從網(wǎng)絡(luò)加載
//運(yùn)行當(dāng)前圖片加載執(zhí)行器
runGenerators();
break;
case DECODE_DATA:
//當(dāng)前狀態(tài)為解析圖片數(shù)據(jù)
//從獲取的數(shù)據(jù)源中解析圖片
decodeFromRetrievedData();
break;
default:
//未知的狀態(tài)辫红,拋出異常
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
步驟6.1中知道凭涂,INITIALIZE之后的狀態(tài)就是從磁盤(pán)緩存中讀取轉(zhuǎn)換后圖片的狀態(tài),我們看下getNextGenerator的實(shí)現(xiàn):
private DataFetcherGenerator getNextGenerator() {
switch (stage) {
case RESOURCE_CACHE:
//當(dāng)前狀態(tài)為從磁盤(pán)緩存中讀取轉(zhuǎn)換后圖片狀態(tài)贴妻,創(chuàng)建一個(gè)從磁盤(pán)緩存讀取轉(zhuǎn)換后圖片的執(zhí)行器
return new ResourceCacheGenerator(decodeHelper, this);
case DATA_CACHE:
//當(dāng)前狀態(tài)為從磁盤(pán)緩存中讀取原始圖片狀態(tài)切油,創(chuàng)建一個(gè)從磁盤(pán)緩存讀取原始圖片的執(zhí)行器
return new DataCacheGenerator(decodeHelper, this);
case SOURCE:
//當(dāng)前狀態(tài)為從網(wǎng)絡(luò)讀取狀態(tài),創(chuàng)建一個(gè)從網(wǎng)絡(luò)讀取的執(zhí)行器
return new SourceGenerator(decodeHelper, this);
case FINISHED:
//當(dāng)前狀態(tài)為完成狀態(tài)名惩,返回空
return null;
default:
//未知狀態(tài)澎胡,拋出異常
throw new IllegalStateException("Unrecognized stage: " + stage);
}
}
創(chuàng)建一個(gè)圖片加載的執(zhí)行器之后,接著就是運(yùn)行執(zhí)行器娩鹉,我們看下runGenerators的源碼:
private void runGenerators() {
//獲取當(dāng)前線(xiàn)程
currentThread = Thread.currentThread();
//記錄當(dāng)前獲取的時(shí)間
startFetchTime = LogTime.getLogTime();
//開(kāi)始獲取狀態(tài)
boolean isStarted = false;
while (!isCancelled && currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
//如果當(dāng)前未取消攻谁、執(zhí)行不為空、并且執(zhí)行器執(zhí)行失敗弯予,則
//獲取新的狀態(tài)
stage = getNextStage(stage);
//獲取新的狀態(tài)對(duì)應(yīng)的圖片加載執(zhí)行器
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
//如果當(dāng)前新的狀態(tài)從網(wǎng)絡(luò)讀取戚宦,則重新規(guī)劃圖片加載的請(qǐng)求
reschedule();
return;
}
}
// We've run out of stages and generators, give up.
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
//狀態(tài)已完成、已取消圖片加載锈嫩、執(zhí)行器執(zhí)行失敗受楼,通知失敗
notifyFailed();
}
// Otherwise a generator started a new load and we expect to be called back in
// onDataFetcherReady.
}
根據(jù)上面的代碼分析,我們可以知道優(yōu)先從磁盤(pán)緩存中讀取轉(zhuǎn)換后圖片呼寸,我們看下磁盤(pán)緩存中讀取轉(zhuǎn)換后圖片執(zhí)行器ResourceCacheGenerator
7.2.1那槽、ResourceCacheGenerator的startNext
public boolean startNext() {
//獲取磁盤(pán)緩存按鍵列表
List<Key> sourceIds = helper.getCacheKeys();
if (sourceIds.isEmpty()) {
//如果按鍵列表為空,代表沒(méi)有磁盤(pán)緩存等舔,返回執(zhí)行失敗
return false;
}
//返回磁盤(pán)資源類(lèi)型列表
List<Class<?>> resourceClasses = helper.getRegisteredResourceClasses();
if (resourceClasses.isEmpty()) {
if (File.class.equals(helper.getTranscodeClass())) {
return false;
}
//如果磁盤(pán)資源類(lèi)型列表為空拋出異常
// TODO(b/73882030): This case gets triggered when it shouldn't. With this assertion it causes
// all loads to fail. Without this assertion it causes loads to miss the disk cache
// unnecessarily
// throw new IllegalStateException(
// "Failed to find any load path from " + helper.getModelClass() + " to "
// + helper.getTranscodeClass());
}
while (modelLoaders == null || !hasNextModelLoader()) {
//如果圖片加載器為空或者沒(méi)有下一個(gè)圖片加載器
resourceClassIndex++;
if (resourceClassIndex >= resourceClasses.size()) {
sourceIdIndex++;
if (sourceIdIndex >= sourceIds.size()) {
return false;
}
resourceClassIndex = 0;
}
Key sourceId = sourceIds.get(sourceIdIndex);
Class<?> resourceClass = resourceClasses.get(resourceClassIndex);
Transformation<?> transformation = helper.getTransformation(resourceClass);
// PMD.AvoidInstantiatingObjectsInLoops Each iteration is comparatively expensive anyway,
// we only run until the first one succeeds, the loop runs for only a limited
// number of iterations on the order of 10-20 in the worst case.
//生成新的磁盤(pán)緩存key
currentKey =
new ResourceCacheKey(// NOPMD AvoidInstantiatingObjectsInLoops
helper.getArrayPool(),
sourceId,
helper.getSignature(),
helper.getWidth(),
helper.getHeight(),
transformation,
resourceClass,
helper.getOptions());
//根據(jù)key獲取磁盤(pán)緩存文件
cacheFile = helper.getDiskCache().get(currentKey);
if (cacheFile != null) {
sourceKey = sourceId;
//根據(jù)文件獲取對(duì)應(yīng)的磁盤(pán)緩存圖片加載器
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
//遍歷磁盤(pán)緩存圖片加載器
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
//圖片加載器構(gòu)建圖片加載對(duì)象
loadData = modelLoader.buildLoadData(cacheFile,
helper.getWidth(), helper.getHeight(), helper.getOptions());
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;
//加載圖片圖片數(shù)據(jù)
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
第一次從磁盤(pán)緩存加載的時(shí)候骚灸,磁盤(pán)緩存為空,所以返回false慌植。至于磁盤(pán)緩存怎么加載圖片甚牲,請(qǐng)查看Glide解析七:磁盤(pán)緩存策略
按照上面的分析結(jié)果,從磁盤(pán)緩存中讀取轉(zhuǎn)換后圖片狀態(tài)之后就是從磁盤(pán)緩存中讀取原始圖片狀態(tài)蝶柿,對(duì)應(yīng)的執(zhí)行器是DataCacheGenerator
7.2.2丈钙、DataCacheGenerator的startNext
public boolean startNext() {
while (modelLoaders == null || !hasNextModelLoader()) {
//如果圖片加載器或者沒(méi)有下一個(gè)圖片加載器
sourceIdIndex++;
if (sourceIdIndex >= cacheKeys.size()) {
return false;
}
//獲取key
Key sourceId = cacheKeys.get(sourceIdIndex);
// PMD.AvoidInstantiatingObjectsInLoops The loop iterates a limited number of times
// and the actions it performs are much more expensive than a single allocation.
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
//創(chuàng)建原始圖片磁盤(pán)緩存key
Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
//獲取磁盤(pán)緩存文件
cacheFile = helper.getDiskCache().get(originalKey);
if (cacheFile != null) {
this.sourceKey = sourceId;
//獲取磁盤(pán)緩存圖片加載器列表
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
//遍歷圖片加載器
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
//構(gòu)建圖片加載對(duì)象
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;
}
與ResourceCacheGenerator一樣,開(kāi)始并不存在磁盤(pán)緩存交汤,所以會(huì)走到從網(wǎng)絡(luò)加載的步驟雏赦,至于磁盤(pán)緩存怎么加載圖片劫笙,請(qǐng)查看Glide解析七:磁盤(pán)緩存策略
7.2.3、從網(wǎng)絡(luò)中加載圖片
步驟7.2知道從網(wǎng)絡(luò)加載圖片使用的執(zhí)行器是SourceGenerator星岗,我們看下SourceGenerator的startNext:
public boolean startNext() {
if (dataToCache != null) {
//dataToCache是網(wǎng)絡(luò)圖片流填大,網(wǎng)絡(luò)圖片流不為空
Object data = dataToCache;
dataToCache = null;
//讀取圖片緩存流,并緩存圖片到磁盤(pán)中
cacheData(data);
}
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
//磁盤(pán)緩存執(zhí)行器不為空俏橘,則執(zhí)行磁盤(pán)緩存執(zhí)行器
return true;
}
sourceCacheGenerator = null;
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
//遍歷網(wǎng)絡(luò)圖片加載器
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
//使用網(wǎng)絡(luò)圖片加載器加載圖片
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
這里的網(wǎng)絡(luò)圖片加載器允华,如果不指定默認(rèn)使用的是HttpUrlFetcher,如果配置了其他的網(wǎng)絡(luò)圖片加載器寥掐,比如OkHttpStreamFetcher靴寂,則使用配置的網(wǎng)絡(luò)加載器,我們這里看HttpUrlFetcher的loadData是怎么加載圖片的:
public void loadData(@NonNull Priority priority,
@NonNull DataCallback<? super InputStream> callback) {
long startTime = LogTime.getLogTime();
try {
//獲取網(wǎng)絡(luò)圖片輸入流
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
//回調(diào)通知網(wǎng)絡(luò)圖片輸入流已經(jīng)就緒
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));
}
}
}
//發(fā)起http請(qǐng)求獲取圖片輸入流
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 {
// Comparing the URLs using .equals performs additional network I/O and is generally broken.
// See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
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.
}
}
//構(gòu)建http請(qǐng)求連接
urlConnection = connectionFactory.build(url);
for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
//添加請(qǐng)求頭
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
//設(shè)置連接召耘、讀超時(shí)時(shí)間
urlConnection.setConnectTimeout(timeout);
urlConnection.setReadTimeout(timeout);
//禁用緩存
urlConnection.setUseCaches(false);
//使用post請(qǐng)求方式
urlConnection.setDoInput(true);
// Stop the urlConnection instance of HttpUrlConnection from following redirects so that
// redirects will be handled by recursive calls to this method, loadDataWithRedirects.
urlConnection.setInstanceFollowRedirects(false);
// Connect explicitly to avoid errors in decoders if connection fails.
//連接http
urlConnection.connect();
// Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352.
//獲取圖片輸入流
stream = urlConnection.getInputStream();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (isHttpOk(statusCode)) {
//http請(qǐng)求成功
return getStreamForSuccessfulRequest(urlConnection);
} else if (isHttpRedirect(statusCode)) {
//重定向重新發(fā)起請(qǐng)求
String redirectUrlString = urlConnection.getHeaderField("Location");
if (TextUtils.isEmpty(redirectUrlString)) {
throw new HttpException("Received empty or null redirect url");
}
URL redirectUrl = new URL(url, redirectUrlString);
// Closing the stream specifically is required to avoid leaking ResponseBodys in addition
// to disconnecting the url connection below. See #2352.
cleanup();
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else if (statusCode == INVALID_STATUS_CODE) {
//網(wǎng)絡(luò)請(qǐng)求失敗百炬,拋出異常
throw new HttpException(statusCode);
} else {
//網(wǎng)絡(luò)請(qǐng)求失敗,拋出異常
throw new HttpException(urlConnection.getResponseMessage(), statusCode);
}
}
從網(wǎng)絡(luò)加載圖片污它,主要是根據(jù)給定的url收壕,發(fā)起http請(qǐng)求,得到網(wǎng)絡(luò)圖片輸入流轨蛤,再回調(diào)onDataReady通知網(wǎng)絡(luò)圖片輸入流已經(jīng)準(zhǔn)備就緒好蜜宪,在回調(diào)中對(duì)圖片輸入流進(jìn)行處理。這里有個(gè)問(wèn)題祥山,為什么不直接在網(wǎng)絡(luò)讀取圖片輸入流之后就對(duì)圖片進(jìn)行解析圃验、緩存等處理?這里Glide遵循了設(shè)計(jì)模式中的單一職責(zé)缝呕,網(wǎng)絡(luò)圖片加載器只負(fù)責(zé)網(wǎng)絡(luò)請(qǐng)求澳窑,具體網(wǎng)絡(luò)響應(yīng)內(nèi)容要怎么處理,是其他模塊的職責(zé)供常。
接下來(lái)我們看下SourceGenerator的onDataReady是怎么處理網(wǎng)絡(luò)圖片輸入流的:
public void onDataReady(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
//需要緩存到磁盤(pán)
//保存網(wǎng)絡(luò)圖片輸入流到dataToCache
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.
//重新規(guī)劃圖片加載
cb.reschedule();
} else {
//不需要緩存到磁盤(pán)摊聋,直接回到圖片已經(jīng)獲取就緒
cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
loadData.fetcher.getDataSource(), originalKey);
}
}
一般情況下默認(rèn)是緩存到磁盤(pán)的,所有我們看cb.reschedule的源碼栈暇,這個(gè)cb就是DecodeJob:
public void reschedule() {
//設(shè)置當(dāng)前的狀態(tài)為轉(zhuǎn)為網(wǎng)絡(luò)圖片流就緒狀態(tài)
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
callback.reschedule(this);
}
DecodeJob的reschedule接著有回到Engine的reschedule麻裁,我們看下Engine的reschedule:
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.
//在ActiveSourceExecutor線(xiàn)程池中運(yùn)行DecodeJob線(xiàn)程
getActiveSourceExecutor().execute(job);
}
接著又進(jìn)入了DecodeJob的run方法,根據(jù)前面對(duì)DecodeJob run方法的分析源祈,它會(huì)進(jìn)入runWrapped的SWITCH_TO_SOURCE_SERVICE分支煎源,SWITCH_TO_SOURCE_SERVICE分支直接調(diào)用runGenerators,而當(dāng)前的圖片執(zhí)行器還是網(wǎng)絡(luò)執(zhí)行器SourceGenerator香缺,接著又執(zhí)行SourceGenerator的startNext方法:
public boolean startNext() {
if (dataToCache != null) {
//dataToCache就是網(wǎng)絡(luò)請(qǐng)求的網(wǎng)絡(luò)圖片輸入流
Object data = dataToCache;
dataToCache = null;
//緩存網(wǎng)絡(luò)圖片輸入流到磁盤(pán)中
cacheData(data);
}
//執(zhí)行原始圖片磁盤(pán)緩存輸入流執(zhí)行器
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
//...
}
繞了一大圈又回到SourceGenerator的startNext手销,在startNext中調(diào)用cacheData對(duì)網(wǎng)絡(luò)圖片輸入流進(jìn)行緩存:
private void cacheData(Object dataToCache) {
long startTime = LogTime.getLogTime();
try {
//獲取圖片編碼器
Encoder<Object> encoder = helper.getSourceEncoder(dataToCache);
//創(chuàng)建圖片數(shù)據(jù)緩存讀寫(xiě)器
DataCacheWriter<Object> writer =
new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());
//創(chuàng)建原始圖片緩存key
originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
//緩存到磁盤(pán)中
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();
}
//創(chuàng)建原始圖片磁盤(pán)緩存執(zhí)行器
sourceCacheGenerator =
new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
}
具體磁盤(pán)是怎么緩存的請(qǐng)查看Glide解析七:磁盤(pán)緩存策略
我們繼續(xù)往下看DataCacheGenerator的startNext,前面我們分析過(guò)DataCacheGenerator的startNext图张,其最終根據(jù)key的磁盤(pán)緩存的文件锋拖,再通過(guò)圖片加載取讀取加載圖片诈悍,使用的圖片加載器就是FileLoader,而FileLoader的fetcher對(duì)應(yīng)的是FileFetcher兽埃,然后使用FileFetcher的loadData加載圖片侥钳,我們看下FileFetcher的loadData方法:
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方法,opener由兩個(gè)工廠類(lèi)創(chuàng)建的讲仰,一個(gè)是FileLoader的靜態(tài)內(nèi)部類(lèi)StreamFactory慕趴,另一個(gè)是FileLoader的靜態(tài)內(nèi)部類(lèi)FileDescriptorFactory痪蝇,我們用StreamFactory來(lái)分析:
public static class StreamFactory extends Factory<InputStream> {
public StreamFactory() {
super(new FileOpener<InputStream>() {
@Override
public InputStream open(File file) throws FileNotFoundException {
//open方法直接就是創(chuàng)建一個(gè)圖片文件輸入流鄙陡,并返回
return new FileInputStream(file);
}
@Override
public void close(InputStream inputStream) throws IOException {
inputStream.close();
}
@Override
public Class<InputStream> getDataClass() {
return InputStream.class;
}
});
}
}
由此可知,F(xiàn)ildeLoader圖片加載器使用FileFetcher的loadData加載圖片主要是獲得圖片的文件輸入流躏啰,并回調(diào)callback的onDataReady方法趁矾,根據(jù)前面的分析流程此callback就是DataCacheGenerator,我們看DataCacheGenerator的onDataReady方法:
public void onDataReady(Object data) {
cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);
}
DataCacheGenerator的onDataReady又回調(diào)SourceGenerator的onDataFetcherReady给僵,我們看下SourceGenerator的onDataFetcherReady:
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
DataSource dataSource, Key attemptedKey) {
// This data fetcher will be loading from a File and provide the wrong data source, so override
// with the data source of the original fetcher
cb.onDataFetcherReady(sourceKey, data, fetcher, loadData.fetcher.getDataSource(), sourceKey);
}
SourceGenerator的onDataFetcherReady又回調(diào)DecodeJob的onDataFetcherReady毫捣,我們接著看DecodeJob的onDataFetcherReady:
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
DataSource dataSource, Key attemptedKey) {
//保存key
this.currentSourceKey = sourceKey;
//保存當(dāng)前圖片文件輸入流
this.currentData = data;
//保存當(dāng)前圖片加載器使用的圖片獲取實(shí)現(xiàn)類(lèi)FileFetcher
this.currentFetcher = fetcher;
//圖片來(lái)源,此處值為DataSource.DATA_DISK_CACHE帝际,來(lái)自磁盤(pán)緩存
this.currentDataSource = dataSource;
this.currentAttemptingKey = attemptedKey;
if (Thread.currentThread() != currentThread) {
//如果不等于當(dāng)前線(xiàn)程
//設(shè)置當(dāng)前運(yùn)行狀態(tài)為圖片解碼
runReason = RunReason.DECODE_DATA;
//重新規(guī)劃圖片加載流程
callback.reschedule(this);
} else {
GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
try {
//從圖片輸入流中對(duì)圖片進(jìn)行解碼
decodeFromRetrievedData();
} finally {
GlideTrace.endSection();
}
}
}
進(jìn)入DecodeJob的onDataFetcherReady蔓同,如果當(dāng)前運(yùn)行線(xiàn)程不等于當(dāng)前線(xiàn)程,那么重新規(guī)劃圖片加載流程蹲诀,接著進(jìn)入DecodeJob的runWrapper方法的DECODE_DATA分支斑粱,DEOCDE_DATA分支也是調(diào)用decodeFromRetrievedData,所以我們接著看decodeFromRetrievedData是怎么對(duì)圖片文件輸入流進(jìn)行處理的:
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 {
//調(diào)用decodeFromData進(jìn)行圖片解碼脯爪,并返回圖片資源對(duì)象
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
//如果圖片資源對(duì)象不為空则北,則根據(jù)解析結(jié)果狀態(tài)進(jìn)行相應(yīng)的操作
notifyEncodeAndRelease(resource, currentDataSource);
} else {
//如果圖片資源對(duì)象為空,代表圖片加載失敗
runGenerators();
}
}
這里通過(guò)decodeFromData方法對(duì)圖片文件輸入流進(jìn)行圖片解析處理痕慢,并返回圖片資源對(duì)象尚揣,通過(guò)notifyEncodeAndRelease通知圖片加載成功或者失敗,并釋放相關(guān)的資源掖举。具體是怎么對(duì)圖片文件輸入流進(jìn)行解析處理的請(qǐng)查看Glide解析八:圖片解析處理
我們接著看notifyEncodeAndRelease方法的實(shí)現(xiàn):
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
//...
notifyComplete(result, dataSource);
//...
}
notifyEncodeAndRelease有調(diào)用notifyComplete方法:
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
setNotifiedOrThrow();
callback.onResourceReady(resource, dataSource);
}
notifyComplete回調(diào)callback的onResourceReady通知圖片資源已經(jīng)加載完成就緒好快骗,這個(gè)callback就是EngineJob,我們繼續(xù)看EngineJob的onResourceReady實(shí)現(xiàn):
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
//保存圖片資源對(duì)象到resource成員變量中
this.resource = resource;
//數(shù)據(jù)來(lái)源保存到dataSource成員變量中
this.dataSource = dataSource;
//發(fā)送MSG_COMPLETE切換到主線(xiàn)程處理
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
接著我們看MAIN_THREAD_HANDLER的MSG_COMPLETE分支:
case MSG_COMPLETE:
job.handleResultOnMainThread();
break;
MSG_COMPLETE分支有調(diào)用EngineJob的handleResultOnMainThread方法:
void handleResultOnMainThread() {
stateVerifier.throwIfRecycled();
if (isCancelled) {
//已取消圖片加載
//回收?qǐng)D片資源
resource.recycle();
//釋放相關(guān)資源
release(false /*isRemovedFromQueue*/);
return;
} else if (cbs.isEmpty()) {
//回調(diào)列表為空塔次,拋出異常
throw new IllegalStateException("Received a resource without any callbacks to notify");
} else if (hasResource) {
throw new IllegalStateException("Already have resource");
}
//將圖片資源構(gòu)成成EngineResource對(duì)象
engineResource = engineResourceFactory.build(resource, isCacheable);
hasResource = true;
// Hold on to resource for duration of request so we don't recycle it in the middle of
// notifying if it synchronously released by one of the callbacks.
//通知圖片加載任務(wù)執(zhí)行完成
engineResource.acquire();
listener.onEngineJobComplete(this, key, engineResource);
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = cbs.size(); i < size; i++) {
ResourceCallback cb = cbs.get(i);
if (!isInIgnoredCallbacks(cb)) {
//調(diào)用回調(diào)列表滨巴,通知圖片資源加載完成且已準(zhǔn)備就緒
engineResource.acquire();
cb.onResourceReady(engineResource, dataSource);
}
}
// Our request is complete, so we can release the resource.
//釋放資源
engineResource.release();
release(false /*isRemovedFromQueue*/);
}
我們的重點(diǎn)在cb的onResourceReady回調(diào),而cb是Engine的load方法參數(shù)俺叭,在Engine load方法中創(chuàng)建EngineJob時(shí)將回調(diào)參數(shù)添加到回調(diào)列表中恭取,前面分析的時(shí)候通過(guò)SingleRequest調(diào)用Engine的load方法并將callback傳給Engine的load方法的,所以最終這個(gè)callback是SingleRequest熄守。我們看下SingleRequest的onResourceReady做什么處理的:
public 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())) {
//請(qǐng)求轉(zhuǎn)換的圖片類(lèi)型與實(shí)際解析得到的圖片類(lèi)型不匹配耗跛,則通知圖片加載失敗
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()) {
//不允許設(shè)置圖片資源的view,釋放相關(guān)的資源
releaseResource(resource);
// We can't put the status to complete before asking canSetResource().
status = Status.COMPLETE;
return;
}
//調(diào)用onResourceReady的重載方法
onResourceReady((Resource<R>) resource, (R) received, dataSource);
}
SingleRequest的onResourceReady中又接著調(diào)用onResourceReady的重載方法:
private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
// We must call isFirstReadyResource before setting status.
boolean isFirstResource = isFirstReadyResource();
//設(shè)置狀態(tài)為加載圖片完成
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) {
//通知圖片加載請(qǐng)求監(jiān)聽(tīng)列表圖片加載完成
anyListenerHandledUpdatingTarget |=
listener.onResourceReady(result, model, target, dataSource, isFirstResource);
}
}
//通知targetListener圖片加載完成
anyListenerHandledUpdatingTarget |=
targetListener != null
&& targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
if (!anyListenerHandledUpdatingTarget) {
Transition<? super R> animation =
animationFactory.build(dataSource, isFirstResource);
//通知target(imageview的包裹類(lèi))圖片加載完成
target.onResourceReady(result, animation);
}
} finally {
isCallingCallbacks = false;
}
//通知圖片加載成功
notifyLoadSuccess();
}
當(dāng)圖片加載完成時(shí)攒发,會(huì)先調(diào)用RequestListener列表通知圖片加載完成调塌,接著回調(diào)TargetListener通知圖片加載完成,最后回調(diào)Target通知圖片加載完成惠猿。在前面分析的時(shí)候Target的具體實(shí)現(xiàn)類(lèi)是DrawableImageViewTarget或者BitmapImageViewTarget羔砾,默認(rèn)是DrawableImageViewTarget,這兩兩者繼承自ImageViewTarget偶妖。我們進(jìn)入ImageViewTarget看下其onResourceReady的實(shí)現(xiàn):
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
if (transition == null || !transition.transition(resource, this)) {
//沒(méi)有動(dòng)畫(huà)
setResourceInternal(resource);
} else {
//有動(dòng)畫(huà)
maybeUpdateAnimatable(resource);
}
}
我們看沒(méi)有動(dòng)畫(huà)的實(shí)現(xiàn)是怎么樣的:
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);
}
調(diào)用的是setResource方法設(shè)置圖片資源姜凄,而setResource是個(gè)抽象方法,具體實(shí)現(xiàn)是在具體實(shí)現(xiàn)類(lèi)DrawableImageViewTarget和BitmapImageViewTarget實(shí)現(xiàn)趾访,我們看下DrawableImageViewTarget的setResource的實(shí)現(xiàn):
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
最終調(diào)用ImageView來(lái)設(shè)置圖片态秧,到這里Glide圖片加載的整體流程就分析講解完,期間涉及到很多點(diǎn)扼鞋,并沒(méi)有展開(kāi)細(xì)說(shuō)申鱼,而是規(guī)劃拆分為各自的點(diǎn)進(jìn)行講解。
我們來(lái)概括性的梳理下Glide的加載流程:
- 先通過(guò)APT在編譯器動(dòng)態(tài)生成GlideApp
- 運(yùn)行期通過(guò)GlideApp的with作為入口返回RequestManager
- 使用RequestManager創(chuàng)建一個(gè)圖片加載請(qǐng)求構(gòu)造器RequestBuilder
- 通過(guò)RequestBuilder設(shè)置圖片加載和解析時(shí)的一些配置信息:比如playholder云头、error捐友、apply、transform溃槐、listener等
- 通過(guò)RequestBuilder的into構(gòu)建具體圖片加載請(qǐng)求對(duì)象默認(rèn)是SingleRequest
- SingleRequest等待ImageView測(cè)量得到具體大小之后匣砖,在通過(guò)圖片加載引起Engine來(lái)加載圖片
- Engine圖片加載引擎根據(jù)四級(jí)緩存策略:優(yōu)先從獲取資源緩存列表加載;接著從緩存列表接著竿痰;在從磁盤(pán)緩存加載脆粥;最后是從網(wǎng)絡(luò)加載
- 最后加載完成先回調(diào)RequestListener通知圖片加載完成;再回調(diào)TargetListener通知圖片加載完成影涉;最后才回調(diào)Target(ImageView的包裹類(lèi))將圖片設(shè)置給ImageView
從Glide的整體流程分析下來(lái)变隔,還是挺復(fù)雜的,為什么Glide要這樣設(shè)計(jì)(或者說(shuō)Glide這樣的框架設(shè)計(jì)有什么巧妙之處)蟹倾?
以我個(gè)人的見(jiàn)解是Glide的這樣的框架設(shè)計(jì)有這樣的巧妙之處:
- 使用方便:使用了鏈?zhǔn)较辉怠㈨憫?yīng)式的思想,給使用者帶來(lái)了使用上的便捷
- 封裝安全性:動(dòng)態(tài)生成GlideApp間接訪(fǎng)問(wèn)Glide
- 模塊鲜棠、職責(zé)劃分清晰:使用了各種設(shè)計(jì)模式(單例肌厨、工廠、觀察者豁陆、構(gòu)建柑爸、適配器、狀態(tài)等)使模塊和相應(yīng)的職責(zé)劃分比較清晰盒音、耦合度也比較低
- 擴(kuò)展性強(qiáng):Glide的擴(kuò)展性設(shè)計(jì)的還是很強(qiáng)的表鳍,這也是Glide的一個(gè)突出的特點(diǎn)馅而,它提供很多可擴(kuò)展的功能給開(kāi)發(fā)者,比如:自定義圖片加載器譬圣、自定義圖片解析處理瓮恭、設(shè)置緩存大小、設(shè)置緩存路徑等等
Glide有哪些亮點(diǎn)厘熟,為什么很多人喜歡用屯蹦?
- 使用方便
- 擴(kuò)展性強(qiáng)
- 四級(jí)緩存策略,提供效率和內(nèi)存利用率
- 自動(dòng)感應(yīng)生命周期
- 高性能:運(yùn)用了各種緩存绳姨、復(fù)用策略(LruCache登澜、Pool等),減少對(duì)象的頻繁回收和創(chuàng)建就缆,避免內(nèi)存抖動(dòng)帖渠、對(duì)象頻繁回收導(dǎo)致頻繁GC引起的卡頓