功能介紹
使用文章介紹以及和Picasso的對(duì)比分析請(qǐng)參考Introduction to Glide, Image Loader Library for Android, recommended by Google
由于這篇文章使用glide的老版本,因此有些使用方法可能不太一致了,
Android Glide源碼解析
。本文基于github上Glide最新代碼4.0.0版本做解析。
最基本的使用方式如下:
<code avrasm="" class="hljs">Glide.with(this) .asDrawable() .load(http://i6.topit.me/6/5d/45/1131907198420455d6o.jpg) .apply(fitCenterTransform(this)) .apply(placeholderOf(R.drawable.skyblue_logo_wechatfavorite_checked)) .into(imageView);</code>
Glide使用了現(xiàn)在非常流行的流氏編碼方式,方便了開(kāi)發(fā)者的使用,簡(jiǎn)明、扼要。
接下來(lái)主要對(duì)上面這一段流氏操作做拆分。
Glide 主入口
這個(gè)類(lèi)有點(diǎn)像門(mén)臉模式的統(tǒng)一代理入口,不過(guò)實(shí)際作用在4.0.0中很多功能都被放到后面的其他類(lèi)中,此類(lèi)關(guān)注的點(diǎn)就很少了。雖然整個(gè)libray的所有需要的組建都在這個(gè)類(lèi)中,但是實(shí)際也只是一個(gè)統(tǒng)一初始化的地方。
RequestManager(Glide.with(…))
這個(gè)類(lèi)主要用于管理和啟動(dòng)Glide的所有請(qǐng)求,可以使用activity,fragment或者連接生命周期的事件去智能的停止,啟動(dòng),和重啟請(qǐng)求。也可以檢索或者通過(guò)實(shí)例化一個(gè)新的對(duì)象,或者使用靜態(tài)的Glide去利用構(gòu)建在Activity和Fragment生命周期處理中。它的方法跟你的Fragment和Activity的是同步的。
RequestBuilder
通用類(lèi),可以處理設(shè)置選項(xiàng),并啟動(dòng)負(fù)載的通用資源類(lèi)型。
在這個(gè)類(lèi)中,主要是應(yīng)用請(qǐng)求的很多選項(xiàng)(如下的選項(xiàng)從字面都能看出具體的用處,在ImageView控件中經(jīng)常也能看到,另外之前版本可不是這么使用的):
<code class="hljs" php="">public final class RequestOptions extends BaseRequestOptions<requestoptions>{ private static RequestOptions skipMemoryCacheTrueOptions; private static RequestOptions skipMemoryCacheFalseOptions; private static RequestOptions fitCenterOptions; private static RequestOptions centerCropOptions; private static RequestOptions circleCropOptions; private static RequestOptions noTransformOptions; private static RequestOptions noAnimationOptions; // ...省略...}</requestoptions></code>
RequestBuilder transition(TransitionOptions transitionOptions){} 這個(gè)方法主要是用于加載對(duì)象從占位符(placeholder)或者縮略圖(thumbnail)到真正對(duì)象加載完成的轉(zhuǎn)場(chǎng)動(dòng)畫(huà)。
RequestBuilder load(…){}多太方法中,這里可以加載很多類(lèi)型的數(shù)據(jù)對(duì)象,可以是String,Uri,F(xiàn)ile,resourceId,byte[]這些。當(dāng)然這些后面對(duì)應(yīng)的編碼方式也是不一樣的。
Target into(…){}這個(gè)方法是觸發(fā)Request真正啟動(dòng)的地方,在上邊的示例中最后就是調(diào)用這個(gè)方法發(fā)起請(qǐng)求。
不得不說(shuō)的registry域,這個(gè)域掛載了很多元件,該注冊(cè)器中囊括了模塊加載器(ModelLoader)、編碼器(Encoder)、資源解碼器(ResourceDecoder)、資源編碼器(ResourceEncoder)、數(shù)據(jù)回轉(zhuǎn)器(DataRewinder)、轉(zhuǎn)碼器(Transcoder)。這些都是Glide在對(duì)資源編解碼中既是基礎(chǔ)又是核心功能。
代碼結(jié)構(gòu)
這里主要列舉一下一些重要的組件以及他們的結(jié)構(gòu)關(guān)系:
ModelLoader
DataFetcher
Target
Resource
ResourceTransformation
Pool
Cache
Decoder
Encoder
把這些組件代碼結(jié)構(gòu)列舉出來(lái)主要是為了讓讀者和使用者一目了然的看到自己需要的一些功能。
執(zhí)行流程
1、根據(jù)不同版本的Fragment創(chuàng)建RequestManagerFragment或者SupportRequestManagerFragment,并加入到對(duì)應(yīng)的FragmentManager中。這兩種Fragment是不帶有任何界面的,主要是用于同步生命周期。具體實(shí)現(xiàn)如下:
<code avrasm="" class="hljs">public static RequestManager with(Context context) { RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(context); }// RequestManagerRetriever.get(...) @TargetApi(Build.VERSION_CODES.HONEYCOMB) public RequestManager get(Activity activity) { if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { return get(activity.getApplicationContext()); } else { assertNotDestroyed(activity); android.app.FragmentManager fm = activity.getFragmentManager(); return fragmentGet(activity, fm, null); } } @TargetApi(Build.VERSION_CODES.HONEYCOMB) RequestManager fragmentGet(Context context, android.app.FragmentManager fm, android.app.Fragment parentHint) { RequestManagerFragment current = getRequestManagerFragment(fm, parentHint); RequestManager requestManager = current.getRequestManager(); if (requestManager == null) { requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode()); current.setRequestManager(requestManager); } return requestManager; } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) RequestManagerFragment getRequestManagerFragment( final android.app.FragmentManager fm, android.app.Fragment parentHint) { RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG); if (current == null) { current = pendingRequestManagerFragments.get(fm); if (current == null) { current = new RequestManagerFragment(); current.setParentFragmentHint(parentHint); pendingRequestManagerFragments.put(fm, current); fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss(); handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget(); } } return current; }</code>
2、創(chuàng)建一個(gè)RequestBuilder,并添加一個(gè)DrawableTransitionOptions類(lèi)型的轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
<code class="hljs" cs="">public RequestBuilder<drawable>asDrawable() { return as(Drawable.class).transition(new DrawableTransitionOptions()); }</drawable></code>
3、加載對(duì)象(model域)
<code class="hljs" cs="">public RequestBuilder<transcodetype>load(@Nullable Object model) { return loadGeneric(model); }private RequestBuilder<transcodetype>loadGeneric(@Nullable Object model) { this.model = model; isModelSet = true; return this; }</transcodetype></transcodetype></code>
4、裝載對(duì)象(包含請(qǐng)求的發(fā)起點(diǎn))。
<code class="hljs" cs="">public<y extends="" transcodetype="">> Y into(@NonNull Y target) { Util.assertMainThread(); Preconditions.checkNotNull(target); if (!isModelSet) { throw new IllegalArgumentException(You must call #load() before calling #into()); } Request previous = target.getRequest(); if (previous != null) { requestManager.clear(target); } requestOptions.lock(); Request request = buildRequest(target); target.setRequest(request); requestManager.track(target, request); return target; }</y></code>
一般而言,大部分使用者都是用來(lái)裝載圖片的,因此都會(huì)調(diào)用如下這個(gè)方法:
<code class="hljs" cs="">public Target<transcodetype>into(ImageView view) { Util.assertMainThread(); Preconditions.checkNotNull(view); if (!requestOptions.isTransformationSet() && requestOptions.isTransformationAllowed() && view.getScaleType() != null) { if (requestOptions.isLocked()) { requestOptions = requestOptions.clone(); } switch (view.getScaleType()) { case CENTER_CROP: requestOptions.optionalCenterCrop(context); break; case FIT_CENTER: case FIT_START: case FIT_END: requestOptions.optionalFitCenter(context); break; //$CASES-OMITTED$ default: // Do nothing. } } return into(context.buildImageViewTarget(view, transcodeClass)); }</transcodetype></code>
這里針對(duì)ImageView的填充方式做了篩選并對(duì)應(yīng)設(shè)置到requestOptions上。最終的是通過(guò)ImageView和轉(zhuǎn)碼類(lèi)型(transcodeClass)創(chuàng)建不通過(guò)的Target(例如Bitmap對(duì)應(yīng)的BitmapImageViewTarget和Drawable對(duì)應(yīng)的DrawableImageViewTarget)
4.1 Request的創(chuàng)建buildRequest(target)。
在Request的創(chuàng)建中會(huì)針對(duì)是否有縮略圖來(lái)創(chuàng)建不同尺寸的請(qǐng)求,縮略圖方法可以使用RequestBuilder.thumbnail(…)方法來(lái)添加上。
Glide中的Request都是使用了SingleRequest類(lèi),當(dāng)然縮略圖采用的是ThumbnailRequestCoordinator類(lèi):
<code class="hljs" cs="">private Request obtainRequest(Target<transcodetype>target, BaseRequestOptionsrequestOptions, RequestCoordinator requestCoordinator, TransitionOptionstransitionOptions, Priority priority, int overrideWidth, int overrideHeight) { requestOptions.lock(); return SingleRequest.obtain( context, model, transcodeClass, requestOptions, overrideWidth, overrideHeight, priority, target, requestListener, requestCoordinator, context.getEngine(), transitionOptions.getTransitionFactory()); }</transcodetype></code>
比較值得推崇的是SingleRequest.obtain寫(xiě)法,個(gè)人認(rèn)為比new關(guān)鍵字更簡(jiǎn)潔明了吧。
target.setRequest(request)也是一個(gè)比較值得注意的地方,如果target是ViewTarget,那么request會(huì)被設(shè)置到View的tag上。這樣其實(shí)是有一個(gè)好處,每一個(gè)View有一個(gè)自己的Request,如果有重復(fù)請(qǐng)求,那么都會(huì)先去拿到上一個(gè)已經(jīng)綁定的Request,并且從RequestManager中清理回收掉。這應(yīng)該是去重的功能。
4.2 requestManager.track(target, request)
這個(gè)方法非常的復(fù)雜,主要用于觸發(fā)請(qǐng)求、編解碼、裝載和緩存這些功能。下面就一步一步來(lái)看吧:
4.2.1 緩存target,并啟動(dòng)Request
<code class="hljs" java="">void track(Targettarget, Request request) { targetTracker.track(target); requestTracker.runRequest(request); } /** * Starts tracking the given request. */ public void runRequest(Request request) { requests.add(request); //添加內(nèi)存緩存 if (!isPaused) { request.begin(); // 開(kāi)始 } else { pendingRequests.add(request); // 掛起請(qǐng)求 } }</code>
繼續(xù)看一下SingleRequest中的begin方法:
<code class="hljs" java="">@Override public void begin() { stateVerifier.throwIfRecycled(); startTime = LogTime.getLogTime(); // 如果model空的,那么是不能執(zhí)行的。 這里的model就是前面提到的RequestBuilder中的model 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; } status = Status.WAITING_FOR_SIZE; // 如果當(dāng)前的View尺寸已經(jīng)加載獲取到了,那么就會(huì)進(jìn)入真正的加載流程。 if (Util.isValidDimensions(overrideWidth, overrideHeight)) { onSizeReady(overrideWidth, overrideHeight); } else { // 反之,當(dāng)前View還沒(méi)有畫(huà)出來(lái),那么是沒(méi)有尺寸的。 // 這里會(huì)調(diào)用到ViewTreeObserver.addOnPreDrawListener。 // 等待View的尺寸都o(jì)k,才會(huì)繼續(xù) target.getSize(this); } // 如果等待和正在執(zhí)行狀態(tài),那么當(dāng)前會(huì)加載占位符Drawable if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE) && canNotifyStatusChanged()) { target.onLoadStarted(getPlaceholderDrawable()); } if (Log.isLoggable(TAG, Log.VERBOSE)) { logV(finished run method in + LogTime.getElapsedMillis(startTime)); } }</code>
接下來(lái)是target.getSize(this)方法。這里主要說(shuō)一下尺寸未加載出來(lái)的情況(ViewTarget.java):
<code class="hljs" java="">void getSize(SizeReadyCallback cb) { int currentWidth = getViewWidthOrParam(); int currentHeight = getViewHeightOrParam(); if (isSizeValid(currentWidth) && isSizeValid(currentHeight)) { cb.onSizeReady(currentWidth, currentHeight); } else { // We want to notify callbacks in the order they were added and we only expect one or two // callbacks to // be added a time, so a List is a reasonable choice. if (!cbs.contains(cb)) { cbs.add(cb); } if (layoutListener == null) { final ViewTreeObserver bserver = view.getViewTreeObserver(); layoutListener = new SizeDeterminerLayoutListener(this); // 繪畫(huà)之前加入尺寸的監(jiān)聽(tīng)。這一點(diǎn)我想大部分Android開(kāi)發(fā)同學(xué)應(yīng)該都知道。 // 接下來(lái)在看看系統(tǒng)觸發(fā)該Listener時(shí)target又干了些什么。 observer.addOnPreDrawListener(layoutListener); } } }private static class SizeDeterminerLayoutListener implements ViewTreeObserver .OnPreDrawListener { // 注意這里是弱引用 private final WeakReference<sizedeterminer>sizeDeterminerRef; public SizeDeterminerLayoutListener(SizeDeterminer sizeDeterminer) { sizeDeterminerRef = new WeakReference<>(sizeDeterminer); } @Override public boolean onPreDraw() { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, OnGlobalLayoutListener called listener= + this); } SizeDeterminer sizeDeterminer = sizeDeterminerRef.get(); if (sizeDeterminer != null) { // 通知SizeDeterminer去重新檢查尺寸,并觸發(fā)后續(xù)操作。 // SizeDeterminer有點(diǎn)像工具類(lèi),又作為尺寸回調(diào)的檢測(cè)接口 sizeDeterminer.checkCurrentDimens(); } return true; } }</sizedeterminer></code>
ok,繼續(xù)回到SingleRequest.onSizeReady方法,主要就是Engine發(fā)起load操作
<code avrasm="" class="hljs">public void onSizeReady(int width, int height) { stateVerifier.throwIfRecycled(); if (Log.isLoggable(TAG, Log.VERBOSE)) { logV(Got onSizeReady in + LogTime.getElapsedMillis(startTime)); } if (status != Status.WAITING_FOR_SIZE) { return; } status = Status.RUNNING; float sizeMultiplier = requestOptions.getSizeMultiplier(); this.width = Math.round(sizeMultiplier * width); this.height = Math.round(sizeMultiplier * height); if (Log.isLoggable(TAG, Log.VERBOSE)) { 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.getOptions(), requestOptions.isMemoryCacheable(), this); if (Log.isLoggable(TAG, Log.VERBOSE)) { logV(finished onSizeReady in + LogTime.getElapsedMillis(startTime)); } }</code>
特別的,所有的操作都是來(lái)之唯一一個(gè)Engine,它的創(chuàng)建是來(lái)至于Glide的初始化。如果有需要修改緩存配置的同學(xué)可以繼續(xù)看一下diskCacheFactory的創(chuàng)建:
<code class="hljs" cs="">if (engine == null) { engine = new Engine(memoryCache, diskCacheFactory, diskCacheExecutor, sourceExecutor); }</code>
繼續(xù)看一下Engine.load的詳細(xì)過(guò)程:
<code class="hljs" lasso="">public<r>LoadStatus load( GlideContext glideContext, Object model, Key signature, int width, int height, ClassresourceClass, Class<r>transcodeClass, Priority priority, DiskCacheStrategy diskCacheStrategy, Map<class<?>, Transformation> transformations, boolean isTransformationRequired, Options options, boolean isMemoryCacheable, ResourceCallback cb) { Util.assertMainThread(); long startTime = LogTime.getLogTime(); // 創(chuàng)建key,這是給每次加載資源的唯一標(biāo)示,電腦資料
《Android Glide源碼解析》(http://www.oriental01.com)。 EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options); // 通過(guò)key查找緩存資源 (PS 這里的緩存主要是內(nèi)存中的緩存,切記,可以查看MemoryCache) EngineResourcecached = loadFromCache(key, isMemoryCacheable); if (cached != null) { // 如果有,那么直接利用當(dāng)前緩存的資源。 cb.onResourceReady(cached, DataSource.MEMORY_CACHE); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey(Loaded resource from cache, startTime, key); } return null; } // 這是一個(gè)二級(jí)內(nèi)存的緩存引用,很簡(jiǎn)單用了一個(gè)Map<key,>>>裝載起來(lái)的。 // 這個(gè)緩存主要是誰(shuí)來(lái)放進(jìn)去呢? 可以參考上面一級(jí)內(nèi)存緩存loadFromCache方法。 EngineResourceactive = loadFromActiveResources(key, isMemoryCacheable); if (active != null) { cb.onResourceReady(active, DataSource.MEMORY_CACHE); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey(Loaded resource from active resources, startTime, key); } return null; } // 根據(jù)key獲取緩存的job。 EngineJob current = jobs.get(key); if (current != null) { current.addCallback(cb); // 給當(dāng)前job添加上回調(diào)Callback if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey(Added to existing load, startTime, key); } return new LoadStatus(cb, current); } // 創(chuàng)建job EngineJob<r>engineJob = engineJobFactory.build(key, isMemoryCacheable); DecodeJob<r>decodeJob = decodeJobFactory.build( glideContext, model, key, signature, width, height, resourceClass, transcodeClass, priority, diskCacheStrategy, transformations, isTransformationRequired, options, engineJob); jobs.put(key, engineJob); engineJob.addCallback(cb); // 放入線(xiàn)程池,執(zhí)行 engineJob.start(decodeJob); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey(Started new load, startTime, key); } return new LoadStatus(cb, engineJob); }</r></r></key,></class<?></r></r></code>
上面有一些值得注意的地方:
內(nèi)存緩存:在Glide中默認(rèn)是LruResourceCache。當(dāng)然你也可以自定義; 為何要兩級(jí)內(nèi)存緩存(loadFromActiveResources)。個(gè)人理解是一級(jí)緩存采用LRU算法進(jìn)行緩存,并不能保證全部能命中,添加二級(jí)緩存提高命中率之用; EngineJob和DecodeJob各自職責(zé):EngineJob充當(dāng)了管理和調(diào)度者,主要負(fù)責(zé)加載和各類(lèi)回調(diào)通知;DecodeJob是真正干活的勞動(dòng)者,這個(gè)類(lèi)實(shí)現(xiàn)了Runnable接口。下面來(lái)看看DecodeJob是如何執(zhí)行的:
<code class="hljs" cs="">private void runWrapped() { switch (runReason) { case INITIALIZE: // 初始化 獲取下一個(gè)階段狀態(tài) stage = getNextStage(Stage.INITIALIZE); currentGenerator = getNextGenerator(); // 運(yùn)行 runGenerators(); break; case SWITCH_TO_SOURCE_SERVICE: runGenerators(); break; case DECODE_DATA: decodeFromRetrievedData(); break; default: throw new IllegalStateException(Unrecognized run reason: + runReason); } }// 這里的階段策略首先是從resource中尋找,然后再是data,,再是sourceprivate Stage getNextStage(Stage current) { switch (current) { case INITIALIZE: // 根據(jù)定義的緩存策略來(lái)回去下一個(gè)狀態(tài) // 緩存策略來(lái)之于RequestBuilder的requestOptions域 // 如果你有自定義的策略,可以調(diào)用RequestBuilder.apply方法即可 // 詳細(xì)的可用緩存策略請(qǐng)參看DiskCacheStrategy.java return diskCacheStrategy.decodeCachedResource() ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE); case RESOURCE_CACHE: return diskCacheStrategy.decodeCachedData() ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE); case DATA_CACHE: return Stage.SOURCE; case SOURCE: case FINISHED: return Stage.FINISHED; default: throw new IllegalArgumentException(Unrecognized stage: + current); }// 根據(jù)Stage找到數(shù)據(jù)抓取生成器。private DataFetcherGenerator getNextGenerator() { switch (stage) { case RESOURCE_CACHE: // 產(chǎn)生含有降低采樣/轉(zhuǎn)換資源數(shù)據(jù)緩存文件的DataFetcher。 return new ResourceCacheGenerator(decodeHelper, this); case DATA_CACHE: // 產(chǎn)生包含原始未修改的源數(shù)據(jù)緩存文件的DataFetcher。 return new DataCacheGenerator(decodeHelper, this); case SOURCE: // 生成使用注冊(cè)的ModelLoader和加載時(shí)提供的Model獲取源數(shù)據(jù)規(guī)定的DataFetcher。 // 根據(jù)不同的磁盤(pán)緩存策略,源數(shù)據(jù)可首先被寫(xiě)入到磁盤(pán),然后從緩存文件中加載,而不是直接返回。 return new SourceGenerator(decodeHelper, this); case FINISHED: return null; default: throw new IllegalStateException(Unrecognized stage: + stage); } }</code>
經(jīng)過(guò)很多流程,最后來(lái)到了發(fā)起實(shí)際請(qǐng)求的地方SourceGenerator.startNext()方法:
<code class="hljs" java="">public boolean startNext() { if (dataToCache != null) { Object data = dataToCache; dataToCache = null; cacheData(data); } if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) { return true; } sourceCacheGenerator = null; loadData = null; boolean started = false; // 查找ModelLoader while (!started && hasNextModelLoader()) { loadData = helper.getLoadData().get(loadDataListIndex++); if (loadData != null && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource()) || helper.hasLoadPath(loadData.fetcher.getDataClass()))) { started = true; 根據(jù)model的fetcher加載數(shù)據(jù) loadData.fetcher.loadData(helper.getPriority(), this); } } return started; }</code>
這里的Model必須是實(shí)現(xiàn)了GlideModule接口的,fetcher是實(shí)現(xiàn)了DataFetcher接口。有興趣同學(xué)可以繼續(xù)看一下integration中的okhttp和volley工程。Glide主要采用了這兩種網(wǎng)絡(luò)libray來(lái)下載圖片。
4.2.2 數(shù)據(jù)下載完成后的緩存處理SourceGenerator.onDataReady
<code class="hljs" lasso="">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); } }</code>
有些小伙伴可能看不太明白為什么就一個(gè)dataToCache = data就完了…其實(shí)cb.reschedule()很重要,這里的cb就是DecodeJob.reschedule():
<code class="hljs" cs="">public void reschedule() { runReason = RunReason.SWITCH_TO_SOURCE_SERVICE; callback.reschedule(this); }</code>
這里又有一個(gè)Callback,繼續(xù)追蹤,這里的Callback接口是定義在DecodeJob內(nèi)的,而實(shí)現(xiàn)是在外部的Engine中(這里會(huì)用線(xiàn)程池重新啟動(dòng)當(dāng)前job,那為什么要這樣做呢?源碼中的解釋是為了不同線(xiàn)程的切換,因?yàn)橄螺d都是借用第三方網(wǎng)絡(luò)庫(kù),而實(shí)際的編解碼是在Glide自定義的線(xiàn)程池中進(jìn)行的):
<code class="hljs" cs="">public void reschedule(DecodeJobjob) { if (isCancelled) { MAIN_THREAD_HANDLER.obtainMessage(MSG_CANCELLED, this).sendToTarget(); } else { sourceExecutor.execute(job); } }</code>
接下來(lái)繼續(xù)DecodeJob.runWrapped()方法。這個(gè)時(shí)候的runReason是SWITCH_TO_SOURCE_SERVICE,因此直接執(zhí)行runGenerators(),這里繼續(xù)執(zhí)行SourceGenerator.startNext()方法,值得注意的dataToCache域,因?yàn)樯弦淮螆?zhí)行的時(shí)候是下載,因此再次執(zhí)行的時(shí)候內(nèi)存緩存已經(jīng)存在,因此直接緩存數(shù)據(jù)cacheData(data):
<code class="hljs" cs="">private void cacheData(Object dataToCache) { long startTime = LogTime.getLogTime(); try { // 根據(jù)不同的數(shù)據(jù)獲取注冊(cè)的不同Encoder Encoder<object>encoder = helper.getSourceEncoder(dataToCache); DataCacheWriter<object>writer = new DataCacheWriter<>(encoder, dataToCache, helper.getOptions()); riginalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature()); // 這里的DiskCache實(shí)現(xiàn)是Engine中LazyDiskCacheProvider提供的DiskCacheAdapter。 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)建針對(duì)緩存的Generator sourceCacheGenerator = new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this); }</object></object></code>
繼續(xù)回到SourceGenerator.startNext()方法,這個(gè)時(shí)候已經(jīng)有了sourceCacheGenerator,那么直接執(zhí)行DataCacheGenerator.startNext()方法:
<code class="hljs" cs="">public boolean startNext() { while (modelLoaders == null || !hasNextModelLoader()) { sourceIdIndex++; if (sourceIdIndex >= cacheKeys.size()) { return false; } Key sourceId = cacheKeys.get(sourceIdIndex); Key riginalKey = new DataCacheKey(sourceId, helper.getSignature()); cacheFile = helper.getDiskCache().get(originalKey); if (cacheFile != null) { this.sourceKey = sourceId; modelLoaders = helper.getModelLoaders(cacheFile); modelLoaderIndex = 0; } } loadData = null; boolean started = false; // 這里會(huì)通過(guò)model尋找注冊(cè)過(guò)的ModelLoader while (!started && hasNextModelLoader()) { ModelLoader<file,>modelLoader = modelLoaders.get(modelLoaderIndex++); loadData = modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(), helper.getOptions()); // 通過(guò)FileLoader繼續(xù)加載數(shù)據(jù) if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) { started = true; loadData.fetcher.loadData(helper.getPriority(), this); } } return started; }</file,></code>
這里的ModelLoader跟之前提到過(guò)的Register的模塊加載器(ModelLoader)對(duì)應(yīng)是modelLoaderRegistry域,具體執(zhí)行的操作是Registry.getModelLoaders(…)方法如下:
<code class="hljs" php="">public<model>List<modelloader<model,>> getModelLoaders(Model model) { List<modelloader<model,>> result = modelLoaderRegistry.getModelLoaders(model); if (result.isEmpty()) { throw new NoModelLoaderAvailableException(model); } return result; }</modelloader<model,></modelloader<model,></model></code>
繼續(xù)回到DataCacheGenerator.startNext()方法,找到了ModelLoader,這里筆者跟蹤到的是FileLoader類(lèi)(FileFetcher.loadData(…)方法):
<code class="hljs" lasso="">public void loadData(Priority priority, DataCallbackcallback) { // 讀取文件數(shù)據(jù) 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); }</code>
4.2.3 裝載流程
回調(diào)通知這里就不打算多講了,主要線(xiàn)路如下:
<code class="hljs" haml="">-->DataCacheGenerator.onDataReady -->SourceGenerator.onDataFetcherReady -->DecodeJob.onDataFetcherReady -->DecodeJob.decodeFromRetrievedData -->DecodeJob.notifyEncodeAndRelease -->DecodeJob.notifyComplete -->EngineJob.onResourceReady</code>
Debug流程圖:
需要說(shuō)明的就是在EngineJob中有一個(gè)Handler叫MAIN_THREAD_HANDLER。為了實(shí)現(xiàn)在主UI中裝載資源的作用,ok繼續(xù)上邊的流程:
<code class="hljs" haml="">-->EngineJob.handleResultOnMainThread -->SingleRequest.onResourceReady -->ImageViewTarget.onResourceReady -->ImageViewTarget.setResource -->ImageView.setImageDrawable/ImageView.setImageBitmap</code>
Debug流程圖2:
數(shù)據(jù)的裝載過(guò)程中有一個(gè)很重要的步驟就是decode,這個(gè)操作發(fā)生在DecodeJob.decodeFromRetrievedData的時(shí)候,繼續(xù)看代碼:
<code class="hljs" cs="">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); exceptions.add(e); } if (resource != null) { notifyEncodeAndRelease(resource, currentDataSource); } else { runGenerators(); } }</r></code>
這中間發(fā)生了很多轉(zhuǎn)換主要流程:
<code class="hljs" haml="">-->DecodeJob.decodeFromData-->DecodeJob.decodeFromFetcher-->DecodeJob.runLoadPath -->LoadPath.load -->LoadPath.loadWithExceptionList -->LoadPath.decode -->LoadPath.decodeResource -->LoadPath.decodeResourceWithList -->ResourceDecoder.handles -->ResourceDecoder.decode</code>
這里講到了decode,那么encode發(fā)生在什么時(shí)候呢?直接通過(guò)Encoder接口調(diào)用發(fā)現(xiàn),在數(shù)據(jù)緩存的時(shí)候才會(huì)觸發(fā)編碼。具體調(diào)用在DiskLruCacheWrapper和DataCacheWriter中。一些值得參考的寫(xiě)法例如BitmapEncoder對(duì)Bitmap的壓縮處理。
結(jié)束語(yǔ)
最近看開(kāi)源庫(kù)Glide關(guān)注度一直比較高,因此打算一探究竟。 由于時(shí)間比較緊,因此一些應(yīng)該有的時(shí)序圖沒(méi)有畫(huà),這里也只能簡(jiǎn)單用箭頭代替。不過(guò)個(gè)人認(rèn)為整體執(zhí)行流程已經(jīng)表達(dá)清楚了。
總體來(lái)說(shuō)代碼寫(xiě)的挺漂亮的,單從使用者角度來(lái)說(shuō)入手是比較容易的。 源碼使用了大量的工廠(chǎng)方法來(lái)創(chuàng)建對(duì)象,就像String.valueof(…)方法一樣,這也體現(xiàn)編碼的優(yōu)雅。 不過(guò)想要對(duì)這個(gè)庫(kù)進(jìn)行改造,可能并非易事,筆者在跟蹤代碼的過(guò)程中發(fā)現(xiàn)很多地方有Callback這樣的接口,來(lái)來(lái)回回查找?guī)状魏苋菀拙蜁烆^轉(zhuǎn)向了。。。 另外一個(gè)感覺(jué)難受的地方就是構(gòu)造方法帶入?yún)?shù)太多,就拿SingleRequest來(lái)說(shuō)就是12個(gè)構(gòu)造參數(shù)。 單例的使用感覺(jué)還是有些模糊,就比如GlideContext,有些時(shí)候通過(guò)Glide.get(context).getGlideContext()獲取,而有些類(lèi)中采用構(gòu)造傳入。個(gè)人覺(jué)得既然讓Glide作為單例,那么還這樣傳入?yún)?shù)是不是有點(diǎn)多余?代碼的編寫(xiě)都是可以折中考慮,不過(guò)如果整個(gè)項(xiàng)目擬定好了一個(gè)規(guī)則的話(huà),我想最好還是遵循它。另外再吐槽一下單例,很多開(kāi)發(fā)人員喜歡用單例,如果你是有代碼潔癖的開(kāi)發(fā)者,那么你肯定很討厭這樣,單例很容易造成代碼的散落和結(jié)構(gòu)不清晰。思考
源碼的解析只是把最重要的加載流程走了一遍,有一些比較細(xì)節(jié)的地方?jīng)]有關(guān)注,如果你有需要,可以自己跟著這個(gè)主線(xiàn)debug一下就能查找到。