轉載請標明出處,維權必究: https://www.cnblogs.com/tangZH/p/12543154.html
Glide作為一個強大的圖片載入框架,已經被android官方使用,所以,明白Glide的載入流程以及原理對加深我們對glide的理解是很重要的。
本文基於glide 4.11
Glide.with(this).load("").into(new ImageView(this));
我們從這一句入手,上次我們看了Glide的初始化過程,也就是Glide.with(this)這個方法。現在我們來看into方法。
@NonNull public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) { Util.assertMainThread(); //檢查view是否為null Preconditions.checkNotNull(view); //根據view.getScaleType()設定不同的transform變換,這個transform變換我們單獨講 BaseRequestOptions<?> requestOptions = this; 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. } } return into( //根據transcodeClass的型別構造不同的Target glideContext.buildImageViewTarget(view, transcodeClass), /*targetListener=*/ null, requestOptions, Executors.mainThreadExecutor()); }
構建不同的target
glideContext.buildImageViewTarget(view, transcodeClass),跟著程式碼點進去,最後跟蹤到了這裡:
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)"); } }
如果說我們最終要將資源解碼為bitmap,那麼就構造BitmapImageViewTarget,如果要將資源解碼為Drawable,那麼就構造DrawableImageViewTarget。
private <Y extends Target<TranscodeType>> Y into( @NonNull Y target, @Nullable RequestListener<TranscodeType> targetListener, BaseRequestOptions<?> options, Executor callbackExecutor) { Preconditions.checkNotNull(target); //檢測是否已經呼叫過load方法 if (!isModelSet) { throw new IllegalArgumentException("You must call #load() before calling #into()"); } //構造request Request request = buildRequest(target, targetListener, options, callbackExecutor); //獲取改target是否已經有繫結的request Request previous = target.getRequest(); /** * 這裡修復了一個bug,詳見 https://github.com/bumptech/glide/issues/2270 */ if (request.isEquivalentTo(previous) && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) { // If the request is completed, beginning again will ensure the result is re-delivered, // triggering RequestListeners and Targets. If the request is failed, beginning again will // restart the request, giving it another chance to complete. If the request is already // running, we can let it continue running without interruption. if (!Preconditions.checkNotNull(previous).isRunning()) { // Use the previous request rather than the new one to allow for optimizations like skipping // setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions // that are done in the individual Request. previous.begin(); } return target; } requestManager.clear(target); //將該request設定給target target.setRequest(request); requestManager.track(target, request); return target; }
先看一下buildRequest(target, targetListener, options, callbackExecutor);做了什麼
追蹤進去,呼叫buildRequestRecursive方法。
然後主要是這兩個方法:
Request mainRequest =
buildThumbnailRequestRecursive(
requestLock,
target,
targetListener,
parentCoordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
requestOptions,
callbackExecutor);
Request errorRequest =
errorBuilder.buildRequestRecursive(
requestLock,
target,
targetListener,
errorRequestCoordinator,
errorBuilder.transitionOptions,
errorBuilder.getPriority(),
errorOverrideWidth,
errorOverrideHeight,
errorBuilder,
callbackExecutor);
最後設定給ErrorRequestCoordinator
errorRequestCoordinator.setRequests(mainRequest, errorRequest);
ErrorRequestCoordinator負責管理這些請求,如果請求失敗就執行錯誤的請求。
我們看這個方法:buildThumbnailRequestRecursive
private Request buildThumbnailRequestRecursive( Object requestLock, Target<TranscodeType> target, RequestListener<TranscodeType> targetListener, @Nullable RequestCoordinator parentCoordinator, TransitionOptions<?, ? super TranscodeType> transitionOptions, Priority priority, int overrideWidth, int overrideHeight, BaseRequestOptions<?> requestOptions, Executor callbackExecutor) { if (thumbnailBuilder != null) { // Recursive case: contains a potentially recursive thumbnail request builder. if (isThumbnailBuilt) { throw new IllegalStateException( "You cannot use a request as both the main request and a " + "thumbnail, consider using clone() on the request(s) passed to thumbnail()"); } TransitionOptions<?, ? super TranscodeType> thumbTransitionOptions = thumbnailBuilder.transitionOptions; // Apply our transition by default to thumbnail requests but avoid overriding custom options // that may have been applied on the thumbnail request explicitly. if (thumbnailBuilder.isDefaultTransitionOptionsSet) { thumbTransitionOptions = transitionOptions; } Priority thumbPriority = thumbnailBuilder.isPrioritySet() ? thumbnailBuilder.getPriority() : getThumbnailPriority(priority); int thumbOverrideWidth = thumbnailBuilder.getOverrideWidth(); int thumbOverrideHeight = thumbnailBuilder.getOverrideHeight(); if (Util.isValidDimensions(overrideWidth, overrideHeight) && !thumbnailBuilder.isValidOverride()) { thumbOverrideWidth = requestOptions.getOverrideWidth(); thumbOverrideHeight = requestOptions.getOverrideHeight(); } ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(requestLock, parentCoordinator); Request fullRequest = obtainRequest( requestLock, target, targetListener, requestOptions, coordinator, transitionOptions, priority, overrideWidth, overrideHeight, callbackExecutor); isThumbnailBuilt = true; // Recursively generate thumbnail requests. Request thumbRequest = thumbnailBuilder.buildRequestRecursive( requestLock, target, targetListener, coordinator, thumbTransitionOptions, thumbPriority, thumbOverrideWidth, thumbOverrideHeight, thumbnailBuilder, callbackExecutor); isThumbnailBuilt = false; coordinator.setRequests(fullRequest, thumbRequest); return coordinator; } else if (thumbSizeMultiplier != null) { // Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse. ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(requestLock, parentCoordinator); Request fullRequest = obtainRequest( requestLock, target, targetListener, requestOptions, coordinator, transitionOptions, priority, overrideWidth, overrideHeight, callbackExecutor); BaseRequestOptions<?> thumbnailOptions = requestOptions.clone().sizeMultiplier(thumbSizeMultiplier); Request thumbnailRequest = obtainRequest( requestLock, target, targetListener, thumbnailOptions, coordinator, transitionOptions, getThumbnailPriority(priority), overrideWidth, overrideHeight, callbackExecutor); coordinator.setRequests(fullRequest, thumbnailRequest); return coordinator; } else { // Base case: no thumbnail. return obtainRequest( requestLock, target, targetListener, requestOptions, parentCoordinator, transitionOptions, priority, overrideWidth, overrideHeight, callbackExecutor); } }
首先對縮圖及是否對Target設定引數的判斷(是否使用了thumbnail()方法和sizeMultiplier()方法),如果有使用thunmnail()方法,則生成原始圖片和縮圖的請求,並由ThumbnailRequestCoordinator物件來協調管理,使用了sizeMultiplier()方法,則同樣的處理(前者遞迴的獲得縮圖的Request,後者不遞迴),否則就只生成原始圖片的請求。
他們最終都會呼叫obtainRequest方法,追蹤進去可以發現該方法最終返回的是SingleRequest物件。初始化request的時候傳遞的引數很多:
public static <R> SingleRequest<R> obtain( Context context, GlideContext glideContext, Object requestLock, Object model, Class<R> transcodeClass, BaseRequestOptions<?> requestOptions, int overrideWidth, int overrideHeight, Priority priority, Target<R> target, RequestListener<R> targetListener, @Nullable List<RequestListener<R>> requestListeners, RequestCoordinator requestCoordinator, Engine engine, TransitionFactory<? super R> animationFactory, Executor callbackExecutor) {
1.GlideContext glideContext : 全域性上下文
2.Object model :載入的資源型別
3.Class transcodeClass :轉換的型別
4.RequestOptions requestOptions:設定選項(包括:skipMemoryCache,errorDrawable,placeholder,timeoutOf,encodeFormatOf等等)
5.int overrideWidth:目標寬度在所需資源的畫素點。
6.int overrideHeight:目標高度在所需資源的畫素點。
7. Priority priority:載入的優先順序(IMMEDIATE,HIGH,NORMAL,LOW)
8.Target target:上面剛講過,繫結的target
9.RequestListener requestListener:請求載入時候的監聽器
10.RequestCoordinator requestCoordinator:請求協調器(用來協調具有相同Target的協調器)
11.Engine engine:負責啟動負載和管理活動和快取資源。
12.TransitionFactory<? super R> animationFactory:一個工廠類,可以根據請求的狀態產生不同的轉換。
我們再回到into程式碼中,獲取了request之後我們就要開始請求了。
我們看著一句requestManager.track(target, request);
synchronized void track(@NonNull Target<?> target, @NonNull Request request) { targetTracker.track(target); requestTracker.runRequest(request); }
runRequest就是執行請求的程式碼:
/** Starts tracking the given request. */ public void runRequest(@NonNull Request request) { requests.add(request); if (!isPaused) { request.begin(); } else { request.clear(); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Paused, delaying request"); } pendingRequests.add(request); } }
判斷Glide當前是不是處理暫停狀態,如果不是暫停狀態就呼叫Request的begin()方法來執行Request,否則的話就先將Request新增到待執行佇列裡面,等暫停狀態解除了之後再執行。
我們來看begin方法:
@Override public void begin() { synchronized (requestLock) { assertNotCallingCallbacks(); stateVerifier.throwIfRecycled(); startTime = LogTime.getLogTime(); if (model == null) { if (Util.isValidDimensions(overrideWidth, overrideHeight)) { width = overrideWidth; height = overrideHeight; } // Only log at more verbose log levels if the user has set a fallback drawable, because // fallback Drawables indicate the user expects null models occasionally. int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG; onLoadFailed(new GlideException("Received null model"), logLevel); return; } if (status == Status.RUNNING) { throw new IllegalArgumentException("Cannot restart a running request"); }
如果說這個資源已經被載入過了,那麼我們直接呼叫onResourceReady // If we're restarted after we're complete (usually via something like a notifyDataSetChanged // that starts an identical request into the same Target or View), we can simply use the // resource and size we retrieved the last time around and skip obtaining a new size, starting // a new load etc. This does mean that users who want to restart a load because they expect // that the view size has changed will need to explicitly clear the View or Target before // starting the new load. if (status == Status.COMPLETE) { onResourceReady(resource, DataSource.MEMORY_CACHE); return; } // Restarts for requests that are neither complete nor running can be treated as new requests // and can run again from the beginning. status = Status.WAITING_FOR_SIZE; if (Util.isValidDimensions(overrideWidth, overrideHeight)) { onSizeReady(overrideWidth, overrideHeight); } else { target.getSize(this); } if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE) && canNotifyStatusChanged()) { target.onLoadStarted(getPlaceholderDrawable()); } if (IS_VERBOSE_LOGGABLE) { logV("finished run method in " + LogTime.getElapsedMillis(startTime)); } } }
如果model為null,說明我們沒有呼叫load方法,這時候會回撥onLoadFailed,將status設定為Status.FAILED,然後呼叫setErrorPlaceholder,這個方法裡面最終呼叫target.onLoadFailed(error);將資源置空,然後顯示錯誤圖片。
@Override public void begin() { synchronized (requestLock) { assertNotCallingCallbacks(); stateVerifier.throwIfRecycled(); startTime = LogTime.getLogTime(); if (model == null) { if (Util.isValidDimensions(overrideWidth, overrideHeight)) { width = overrideWidth; height = overrideHeight; } // Only log at more verbose log levels if the user has set a fallback drawable, because // fallback Drawables indicate the user expects null models occasionally. int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG; onLoadFailed(new GlideException("Received null model"), logLevel); return; } if (status == SingleRequest.Status.RUNNING) { throw new IllegalArgumentException("Cannot restart a running request"); } /** * 如果完成後重新啟動(通常是通過notifyDataSetChanged之類的方法 * 將相同的請求傳送到相同的Target或View),我們可以簡單地使用 * 我們最後一次檢索的資源和大小,然後跳過獲取新大小的步驟, * 不用開始一個新的載入。這確實意味著要重新載入的使用者,因為他們 * 更改檢視大小,那麼需要先明確清除view和target,然後 * 開始新的載入。 */ if (status == SingleRequest.Status.COMPLETE) { onResourceReady(resource, DataSource.MEMORY_CACHE); return; } // Restarts for requests that are neither complete nor running can be treated as new requests // and can run again from the beginning. status = SingleRequest.Status.WAITING_FOR_SIZE; /** * 這裡會判斷Util.isValidDimensions(overrideWidth, overrideHeight) * 如果你在使用時候呼叫了override() API為圖片指定了一個固定的寬高,就會按照你給定的去載入;第二種情況是沒有給定的情況, * 那麼target.getSize()方法的內部會根據ImageView的layout_width和layout_height值做一系列的計算,來算出圖片應該的寬高, * 具體計算就在getSize裡面 * 但是不管怎樣,最後都會呼叫onSizeReady()。 */ if (Util.isValidDimensions(overrideWidth, overrideHeight)) { onSizeReady(overrideWidth, overrideHeight); } else { target.getSize(this); } if ((status == SingleRequest.Status.RUNNING || status == SingleRequest.Status.WAITING_FOR_SIZE) && canNotifyStatusChanged()) { target.onLoadStarted(getPlaceholderDrawable()); } if (IS_VERBOSE_LOGGABLE) { logV("finished run method in " + LogTime.getElapsedMillis(startTime)); } } }
我們進去onSizeReady看看
@Override public void onSizeReady(int width, int height) { //如果物件以及被回收了,那麼丟擲異常 stateVerifier.throwIfRecycled(); synchronized (requestLock) { if (IS_VERBOSE_LOGGABLE) { logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime)); } //說明沒有設定大小或者沒有獲取到計算後的大小 if (status != SingleRequest.Status.WAITING_FOR_SIZE) { return; } status = SingleRequest.Status.RUNNING; float sizeMultiplier = requestOptions.getSizeMultiplier(); this.width = maybeApplySizeMultiplier(width, sizeMultiplier); this.height = maybeApplySizeMultiplier(height, sizeMultiplier); if (IS_VERBOSE_LOGGABLE) { logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime)); } loadStatus = engine.load( glideContext, model, requestOptions.getSignature(), this.width, this.height, requestOptions.getResourceClass(), transcodeClass, priority, requestOptions.getDiskCacheStrategy(), requestOptions.getTransformations(), requestOptions.isTransformationRequired(), requestOptions.isScaleOnlyOrNoTransform(), requestOptions.getOptions(), requestOptions.isMemoryCacheable(), requestOptions.getUseUnlimitedSourceGeneratorsPool(), requestOptions.getUseAnimationPool(), requestOptions.getOnlyRetrieveFromCache(), this, callbackExecutor); // This is a hack that's only useful for testing right now where loads complete synchronously // even though under any executor running on any thread but the main thread, the load would // have completed asynchronously. if (status != SingleRequest.Status.RUNNING) { loadStatus = null; } if (IS_VERBOSE_LOGGABLE) { logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime)); } } }
主要的程式碼是engine.load。
/** * /** * 所有的請求流程都如下: * 1.檢查記憶體快取並提供快取的資源 * 2.檢查當前使用的資源,並返回當前的活躍資源 * 3.檢查當前的載入進度,並將cb新增到正在進行的載入進度中 * 4.開始一個新的載入 * * @param glideContext * @param model * @param signature * @param width * @param height * @param resourceClass * @param transcodeClass * @param priority * @param diskCacheStrategy * @param transformations * @param isTransformationRequired * @param isScaleOnlyOrNoTransform * @param options * @param isMemoryCacheable * @param useUnlimitedSourceExecutorPool * @param useAnimationPool * @param onlyRetrieveFromCache * @param cb * @param callbackExecutor * @param <R> * @return */ public <R> Engine.LoadStatus load( GlideContext glideContext, Object model, Key signature, int width, int height, Class<?> resourceClass, Class<R> transcodeClass, Priority priority, DiskCacheStrategy diskCacheStrategy, Map<Class<?>, Transformation<?>> transformations, boolean isTransformationRequired, boolean isScaleOnlyOrNoTransform, Options options, boolean isMemoryCacheable, boolean useUnlimitedSourceExecutorPool, boolean useAnimationPool, boolean onlyRetrieveFromCache, ResourceCallback cb, Executor callbackExecutor) { long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0; //構造一個key EngineKey key = keyFactory.buildKey( model, signature, width, height, transformations, resourceClass, transcodeClass, options); EngineResource<?> memoryResource; synchronized (this) { //通過這個key去快取中看是不是存在資源,loadFromMemory裡面會先去活躍資源快取池中獲取, // 沒有的話再去記憶體快取中獲取,活躍資源即現在正在被其他元件使用的資源。 memoryResource = loadFromMemory(key, isMemoryCacheable, startTime); if (memoryResource == null) { return waitForExistingOrStartNewJob( glideContext, model, signature, width, height, resourceClass, transcodeClass, priority, diskCacheStrategy, transformations, isTransformationRequired, isScaleOnlyOrNoTransform, options, isMemoryCacheable, useUnlimitedSourceExecutorPool, useAnimationPool, onlyRetrieveFromCache, cb, callbackExecutor, key, startTime); } } // Avoid calling back while holding the engine lock, doing so makes it easier for callers to // deadlock. cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE); return null; }
如果找得到資源,那麼就回撥cb.onResourceReady,不然的話會走waitForExistingOrStartNewJob。
我們進去看一下:
private <R> Engine.LoadStatus waitForExistingOrStartNewJob( GlideContext glideContext, Object model, Key signature, int width, int height, Class<?> resourceClass, Class<R> transcodeClass, Priority priority, DiskCacheStrategy diskCacheStrategy, Map<Class<?>, Transformation<?>> transformations, boolean isTransformationRequired, boolean isScaleOnlyOrNoTransform, Options options, boolean isMemoryCacheable, boolean useUnlimitedSourceExecutorPool, boolean useAnimationPool, boolean onlyRetrieveFromCache, ResourceCallback cb, Executor callbackExecutor, EngineKey key, long startTime) { //通過key獲取EngineJob,EngineJob負責開啟執行緒非同步載入。 EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache); if (current != null) { current.addCallback(cb, callbackExecutor); if (VERBOSE_IS_LOGGABLE) { logWithTimeAndKey("Added to existing load", startTime, key); } return new Engine.LoadStatus(cb, current); } //沒有EngineJob則構建一個 EngineJob<R> engineJob = engineJobFactory.build( key, isMemoryCacheable, useUnlimitedSourceExecutorPool, useAnimationPool, onlyRetrieveFromCache); //負責給圖片解碼等一些複雜操作 DecodeJob<R> decodeJob = decodeJobFactory.build( glideContext, model, key, signature, width, height, resourceClass, transcodeClass, priority, diskCacheStrategy, transformations, isTransformationRequired, isScaleOnlyOrNoTransform, onlyRetrieveFromCache, options, engineJob); jobs.put(key, engineJob); engineJob.addCallback(cb, callbackExecutor); //執行 engineJob.start(decodeJob); if (VERBOSE_IS_LOGGABLE) { logWithTimeAndKey("Started new load", startTime, key); } return new Engine.LoadStatus(cb, engineJob); }
public synchronized void start(DecodeJob<R> decodeJob) { this.decodeJob = decodeJob; GlideExecutor executor = decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor(); executor.execute(decodeJob); }
如果要從磁碟快取中去解碼的話,就獲取diskCacheExecutor,否則就用針對原始資源的一個執行器。
在executor.execute(decodeJob)之後便切換到子執行緒了,我們到DecodeJob裡面去看一下。
@Override public void run() { // This should be much more fine grained, but since Java's thread pool implementation silently // swallows all otherwise fatal exceptions, this will at least make it obvious to developers // that something is failing. GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model); // Methods in the try statement can invalidate currentFetcher, so set a local variable here to // ensure that the fetcher is cleaned up either way. DataFetcher<?> localFetcher = currentFetcher; try { if (isCancelled) { notifyFailed(); return; } runWrapped(); } catch (CallbackException e) { // If a callback not controlled by Glide throws an exception, we should avoid the Glide // specific debug logic below. throw e; } catch (Throwable t) { // Catch Throwable and not Exception to handle OOMs. Throwables are swallowed by our // usage of .submit() in GlideExecutor so we're not silently hiding crashes by doing this. We // are however ensuring that our callbacks are always notified when a load fails. Without this // notification, uncaught throwables never notify the corresponding callbacks, which can cause // loads to silently hang forever, a case that's especially bad for users using Futures on // background threads. if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d( TAG, "DecodeJob threw unexpectedly" + ", isCancelled: " + isCancelled + ", stage: " + stage, t); } // When we're encoding we've already notified our callback and it isn't safe to do so again. if (stage != Stage.ENCODE) { throwables.add(t); notifyFailed(); } if (!isCancelled) { throw t; } throw t; } finally { // Keeping track of the fetcher here and calling cleanup is excessively paranoid, we call // close in all cases anyway. if (localFetcher != null) { localFetcher.cleanup(); } GlideTrace.endSection(); } }
主要是runWrapped();
private void runWrapped() { switch (runReason) { case INITIALIZE: stage = getNextStage(Stage.INITIALIZE); currentGenerator = getNextGenerator(); runGenerators(); break; case SWITCH_TO_SOURCE_SERVICE: runGenerators(); break; case DECODE_DATA: decodeFromRetrievedData(); break; default: throw new IllegalStateException("Unrecognized run reason: " + runReason); } }
當INITIALIZE或者SWITCH_TO_SOURCE_SERVICE的時候,走runGenerators()。這兩種是沒有快取的情況下。
runGenerators():
private void runGenerators() { currentThread = Thread.currentThread(); startFetchTime = LogTime.getLogTime(); boolean isStarted = false; while (!isCancelled && currentGenerator != null && !(isStarted = currentGenerator.startNext())) { stage = getNextStage(stage); currentGenerator = getNextGenerator(); if (stage == Stage.SOURCE) { reschedule(); return; } } // We've run out of stages and generators, give up. if ((stage == Stage.FINISHED || isCancelled) && !isStarted) { notifyFailed(); } // Otherwise a generator started a new load and we expect to be called back in // onDataFetcherReady. }
重點:currentGenerator.startNext()。實現startNext方法的有三個:
而我們的currentGenerator是哪一個呢?
回頭看runWrapped
case INITIALIZE: stage = getNextStage(Stage.INITIALIZE); currentGenerator = getNextGenerator(); runGenerators();
點進去getNextGenerator,結合status的值便可以知道返回的是SourceGenerator。(我們討論的是初次載入沒有快取的情況)
我們來到SourceGenerator的startNext()方法:
@Override public boolean startNext() { if (dataToCache != null) { Object data = dataToCache; dataToCache = null; cacheData(data); } if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) { return true; } sourceCacheGenerator = null; loadData = null; boolean started = false; while (!started && hasNextModelLoader()) { loadData = helper.getLoadData().get(loadDataListIndex++); if (loadData != null && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource()) || helper.hasLoadPath(loadData.fetcher.getDataClass()))) { started = true; startNextLoad(loadData); } } return started; }
如果sourceCacheGenerator 不為null,就呼叫它的startNext,在裡面去獲取modelLoader,然後去載入資源,modelLoader即模型載入器,Glide初始化的時候註冊了很多模型載入器。
registry .append(int.class, InputStream.class, resourceLoaderStreamFactory) .append(int.class, ParcelFileDescriptor.class, resourceLoaderFileDescriptorFactory) .append(Integer.class, InputStream.class, resourceLoaderStreamFactory) .append(Integer.class, ParcelFileDescriptor.class, resourceLoaderFileDescriptorFactory) .append(Integer.class, Uri.class, resourceLoaderUriFactory) .append(int.class, AssetFileDescriptor.class, resourceLoaderAssetFileDescriptorFactory) .append(Integer.class, AssetFileDescriptor.class, resourceLoaderAssetFileDescriptorFactory) .append(int.class, Uri.class, resourceLoaderUriFactory) .append(String.class, InputStream.class, new DataUrlLoader.StreamFactory<String>()) .append(Uri.class, InputStream.class, new DataUrlLoader.StreamFactory<Uri>())
如append(Uri.class, InputStream.class, new DataUrlLoader.StreamFactory<Uri>())
將Uri物件轉換為InputStream,模型載入器為DataUrlLoader.StreamFactory,也就是說我們載入的時候如果傳進來的是一個uri物件,那麼最終會被轉換為InputStream。
我們不進去看,直接看接下來的程式碼。
loadData = helper.getLoadData().get(loadDataListIndex++);
loadData裡面包含著:
sourceKey:標識這個載入的原始資源的key
alternateKeys:備用的快取key指向相同的資料
DataFetcher:用來獲取沒有在快取中發現的資料(即需要去載入的,modelLoader中都包含著這個)
接下來看:
startNextLoad(loadData);
private void startNextLoad(final LoadData<?> toStart) { loadData.fetcher.loadData( helper.getPriority(), new DataCallback<Object>() { @Override public void onDataReady(@Nullable Object data) { if (isCurrentRequest(toStart)) { onDataReadyInternal(toStart, data); } } @Override public void onLoadFailed(@NonNull Exception e) { if (isCurrentRequest(toStart)) { onLoadFailedInternal(toStart, e); } } }); }
loadData.fetcher.loadData:這裡是我們真正去載入資源的地方。
點選進去loadeData,發現好多實現了該方法的類。那我們這裡的Fetcher究竟是哪一個呢?
首先從名字來看,如果我們載入的是網路資源,那麼就是:HttpUrlFetcher。
這個HttpUrlFetcher跟我們的modelLoader是什麼關係呢
我們可以看出LoadData在ModelLoader類中。
檢視HttpUrlFetcher的呼叫可以追溯到HttpGlideUrlLoader。
@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)); }
我們可以看出HttpGlideUrlLoader實現了ModelLoader的方法,buildLoadData,而在buildLoadData中返回了一個LoadData物件,這個物件傳入的就是HttpUrlFetcher。
這個buildLoadData什麼時候被呼叫的呢?
我們回到startNext方法,loadData = helper.getLoadData().get(loadDataListIndex++);的getLoadData()裡面:
好,那麼我們看HttpUrlFetcher的loadData();
@Override public void loadData( @NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) { long startTime = LogTime.getLogTime(); try { InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders()); callback.onDataReady(result); } catch (IOException e) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Failed to load data for url", e); } callback.onLoadFailed(e); } finally { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime)); } } }
loadDataWithRedirects返回了InputStream,然後callback回撥。
我們進去loadDataWithRedirects看可以發現這個方法其實是去請求網路,我們就不細看了。
private void startNextLoad(final LoadData<?> toStart) { loadData.fetcher.loadData( helper.getPriority(), new DataCallback<Object>() { @Override public void onDataReady(@Nullable Object data) { if (isCurrentRequest(toStart)) { onDataReadyInternal(toStart, data); } } @Override public void onLoadFailed(@NonNull Exception e) { if (isCurrentRequest(toStart)) { onLoadFailedInternal(toStart, e); } } }); }
回撥後呼叫onDataReadyInternal(toStart, data);
且看一下:
@SuppressWarnings("WeakerAccess") @Synthetic void onDataReadyInternal(LoadData<?> loadData, 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); } }
我們看這個方法:onDataFetcherReady
檢視一下便可以知道會回撥DecodeJob中的方法:
@Override public void onDataFetcherReady( Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) { this.currentSourceKey = sourceKey; this.currentData = data; this.currentFetcher = fetcher; this.currentDataSource = dataSource; this.currentAttemptingKey = attemptedKey; if (Thread.currentThread() != currentThread) { runReason = RunReason.DECODE_DATA; callback.reschedule(this); } else { GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData"); try { decodeFromRetrievedData(); } finally { GlideTrace.endSection(); } } }
到此我們就完成了加網路資源的過程,接下來就是解碼等等的操作了。
我們看decodeFromRetrievedData:
裡面有一句:
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
將資源解碼成Resource。
我們追蹤進去:
decodeFromData -> decodeFromFetcher -> runLoadPath -> path.load -> loadWithExceptionList
loadWithExceptionList裡面便開始進行我們的解碼操作了。
private Resource<Transcode> loadWithExceptionList( DataRewinder<Data> rewinder, @NonNull Options options, int width, int height, DecodePath.DecodeCallback<ResourceType> decodeCallback, List<Throwable> exceptions) throws GlideException { Resource<Transcode> result = null; //noinspection ForLoopReplaceableByForEach to improve perf for (int i = 0, size = decodePaths.size(); i < size; i++) { DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i); try { result = path.decode(rewinder, width, height, options, decodeCallback); } catch (GlideException e) { exceptions.add(e); } if (result != null) { break; } }
我們看:
result = path.decode(rewinder, width, height, options, decodeCallback);
public Resource<Transcode> decode( DataRewinder<DataType> rewinder, int width, int height, @NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException { Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options); Resource<ResourceType> transformed = callback.onResourceDecoded(decoded); return transcoder.transcode(transformed, options); }
我們看decodeResource -> decodeResourceWithList
@NonNull private Resource<ResourceType> decodeResourceWithList( DataRewinder<DataType> rewinder, int width, int height, @NonNull Options options, List<Throwable> exceptions) throws GlideException { Resource<ResourceType> result = null; //noinspection ForLoopReplaceableByForEach to improve perf //遍歷獲取解碼器 for (int i = 0, size = decoders.size(); i < size; i++) { ResourceDecoder<DataType, ResourceType> decoder = decoders.get(i); try { DataType data = rewinder.rewindAndGet(); //該解碼器是否可以解碼該資料 if (decoder.handles(data, options)) { //獲取資料 data = rewinder.rewindAndGet(); //解碼 result = decoder.decode(data, width, height, options); } // Some decoders throw unexpectedly. If they do, we shouldn't fail the entire load path, but // instead log and continue. See #2406 for an example. } catch (IOException | RuntimeException | OutOfMemoryError e) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Failed to decode data for " + decoder, e); } exceptions.add(e); } if (result != null) { break; } } if (result == null) { throw new GlideException(failureMessage, new ArrayList<>(exceptions)); } return result; }
獲取對應的解碼器後用decode方法進行解碼。Glide初始化的時候便註冊了一大堆解碼器,如:
.append( Registry.BUCKET_GIF, InputStream.class, GifDrawable.class, new StreamGifDecoder(imageHeaderParsers, byteBufferGifDecoder, arrayPool))
對於Gif型別,將InputStream解碼為GifDrawable,解碼器為StreamGifDecoder
接下來我們回到這個方法:
public Resource<Transcode> decode( DataRewinder<DataType> rewinder, int width, int height, @NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException { Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options); Resource<ResourceType> transformed = callback.onResourceDecoded(decoded); return transcoder.transcode(transformed, options); }
Resource<ResourceType> transformed = callback.onResourceDecoded(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; if (dataSource != DataSource.RESOURCE_DISK_CACHE) { appliedTransformation = decodeHelper.getTransformation(resourceSubClass); transformed = appliedTransformation.transform(glideContext, decoded, width, height); } // TODO: Make this the responsibility of the Transformation. if (!decoded.equals(transformed)) { decoded.recycle(); } final EncodeStrategy encodeStrategy; final ResourceEncoder<Z> encoder; if (decodeHelper.isResourceEncoderAvailable(transformed)) { encoder = decodeHelper.getResultEncoder(transformed); encodeStrategy = encoder.getEncodeStrategy(options); } else { encoder = null; encodeStrategy = EncodeStrategy.NONE; } Resource<Z> result = transformed; boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey); if (diskCacheStrategy.isResourceCacheable( isFromAlternateCacheKey, dataSource, encodeStrategy)) { if (encoder == null) { throw new Registry.NoResultEncoderAvailableException(transformed.get().getClass()); } final Key key; switch (encodeStrategy) { case SOURCE: key = new DataCacheKey(currentSourceKey, signature); break; case TRANSFORMED: key = new ResourceCacheKey( decodeHelper.getArrayPool(), currentSourceKey, signature, width, height, appliedTransformation, resourceSubClass, options); break; default: throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy); } LockedResource<Z> lockedResult = LockedResource.obtain(transformed); deferredEncodeManager.init(key, encoder, lockedResult); result = lockedResult; } return result; }
首先獲取Transformation,然後呼叫transform方法進行變換處理。
返回resource之後回到decodeFromRetrievedData方法。
private void decodeFromRetrievedData() { if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey( "Retrieved data", startFetchTime, "data: " + currentData + ", cache key: " + currentSourceKey + ", fetcher: " + currentFetcher); } Resource<R> resource = null; try { resource = decodeFromData(currentFetcher, currentData, currentDataSource); } catch (GlideException e) { e.setLoggingDetails(currentAttemptingKey, currentDataSource); throwables.add(e); } if (resource != null) { notifyEncodeAndRelease(resource, currentDataSource); } else { runGenerators(); } }
我們沿著方法進入notifyEncodeAndRelease ---> notifyComplete ----> onResourceReady ------> notifyCallbacksOfResult
。。。。算了寫的到這裡好累,接下來不寫了自己看,就是去設定資源。