寫在以前html
上一篇博文寫的是Android 圖片加載框架Glide4.0源碼徹底解析(一),主要分析了Glide4.0源碼中的with方法和load方法,本來打算是一塊兒發佈的,可是因爲into方法複雜性遠不是前兩個方法所能比擬的,又不肯意馬馬虎虎的隨便應付的寫做,仍是保持一向的一步步深刻的講解,因此就提早發佈了一篇,以減小篇幅。java
正文python
這篇是講Glide源碼中into方法的實現原理,能夠說with和load方法只是作了前期的初始化配置工做,而真正意義上的圖片加載就是在into方法中實現的,因此該方法的複雜程度是能夠想象的,仍是依照我以前的寫做習慣,一步步的分析,不留下任何的盲點給你們帶來困惑,那麼下面就開始吧。緩存
前面兩個方法把所需的基礎配置基本已作好,那麼接下來就是真正的要去加載資源了,那麼咱們來看看吧:微信
首先進去into方法中:網絡
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(); break; case CENTER_INSIDE: requestOptions.optionalCenterInside(); break; case FIT_CENTER: case FIT_START: case FIT_END: requestOptions.optionalFitCenter(); break; case FIT_XY: requestOptions.optionalCenterInside(); break; case CENTER: case MATRIX: default: // Do nothing. } } return into(context.buildImageViewTarget(view, transcodeClass)); }
主要是爲requestOptions 作一些配置,這個配置時根據View的屬性而來的。app
而後調用GlideContext的buildImageViewTarget方法,並把view和transcodeClass傳遞進去來構造一個viewTarget的對象,跟進去看看:框架
直接的調用imageViewTargetFactory的buildTarget方法,而後看下buildTarget的源碼:ide
還記得我屢次提醒transcodeClass是什麼嗎?沒錯就是Drawable.class,就是在這個分支中使用到,那麼在buildTarget中就是建立一個DrawableImageViewTarget對象,並它view傳遞進去,DrawableImageViewTarget繼承於ImageViewTarget,又把view傳遞到了ViewTarget中,最終把view存放到了ViewTarget中的view變量中,而且還建立了一個SizeDeterminer對象:oop
ok,這就是咱們的ImageView的最終去向,知道了view的去向,咱們在返回到RequestBuilder中,建立個DrawableImageViewTarget對象後,又把它重定向到into方法中:
這個方法是很是重要的,必需要理解清楚,其實真正的理解好了也就很簡單的了:
首先,從target中調用getRequest方法獲取Request請求,這個getRequest方法是在ViewTarget父類中:
它首先從View中獲取tag標識,可是咱們從未爲view設置標識,它也就不存在什麼標識,因此getTag方法會返回一個null值,由此request也就是一個null空值。
在從taget中獲取不到request,那麼就須要爲它去構造request對象,因此它調用了buildRequest:
private Request buildRequest(Target<TranscodeType> target) { return buildRequestRecursive(target, null, transitionOptions, requestOptions.getPriority(), requestOptions.getOverrideWidth(), requestOptions.getOverrideHeight()); }
buildRequest方法又調用了buildRequestRecursive方法:
private Request buildRequestRecursive(Target<TranscodeType> target, @Nullable ThumbnailRequestCoordinator parentCoordinator, TransitionOptions<?, ? super TranscodeType> transitionOptions, Priority priority, int overrideWidth, int overrideHeight) { ... return obtainRequest(target, requestOptions, parentCoordinator, transitionOptions, priority, overrideWidth, overrideHeight); }
buildRequestRecursive作了一堆判斷,最終會調用obtainRequest方法:
在這裏它直接的調用SingleRequest.obtain方法,並返回一個Request對象,由此咱們看到最終是調用到SingleRequest來真正的構造Request對象:
public static <R> SingleRequest<R> obtain( GlideContext glideContext, Object model, Class<R> transcodeClass, RequestOptions requestOptions, int overrideWidth, int overrideHeight, Priority priority, Target<R> target, RequestListener<R> requestListener, RequestCoordinator requestCoordinator, Engine engine, TransitionFactory<? super R> animationFactory) { @SuppressWarnings("unchecked") SingleRequest<R> request = (SingleRequest<R>) POOL.acquire(); if (request == null) { request = new SingleRequest<>(); } request.init( glideContext, model, transcodeClass, requestOptions, overrideWidth, overrideHeight, priority, target, requestListener, requestCoordinator, engine, animationFactory); return request; }
由obtain方法看到,建立了一個SingleRequest對象,並調用它的init方法來進行初始化操做。
ok,到此Request建立完畢,來看看它擁有哪些成員:
讓咱們再回到into方法中,建立了Request對象後,把它設置給咱們的target,最終則是調用setTag方法爲view設置tag:
private void setTag(@Nullable Object tag) { if (tagId == null) { isTagUsedAtLeastOnce = true; view.setTag(tag); } else { view.setTag(tagId, tag); } }
設置完畢以後將會調用requestManager中的track方法:
void track(Target<?> target, Request request) { targetTracker.track(target); requestTracker.runRequest(request); }
track方法中分別把target和request添加到targetTracker和requestTracker追蹤器中。
而後來看看runRequest方法:
它將會調用Request對象的begin方法來企圖開啓咱們的請求,咱們知道request其實就是咱們的SingleRequest對象,那麼到它的begin方法中看看到底作了哪些事情:
@Override public void begin() { 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; } 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 (Log.isLoggable(TAG, Log.VERBOSE)) { logV("finished run method in " + LogTime.getElapsedMillis(startTime)); } }
首先,它判斷model是否爲空,若是請求url爲空的話,那就不用請求了,直接調用onLoadFailed加載失敗。
而後,調用onSizeReady方法,真正的資源加載就是從這個方法中開始的。
最後,根據Status對象狀態和是否有佔位圖來設置加載過程當中的佔位圖。
那麼重點就是在onSizeReady方法中了,咱們來詳細的看看它的源碼:
@Override 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 = maybeApplySizeMultiplier(width, sizeMultiplier); this.height = maybeApplySizeMultiplier(height, sizeMultiplier); 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(), requestOptions.getUseUnlimitedSourceGeneratorsPool(), requestOptions.getOnlyRetrieveFromCache(), this); if (Log.isLoggable(TAG, Log.VERBOSE)) { logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime)); } }
onSizeReady方法中首先把State狀態更改成RUNNING,而後獲取到ImageView的寬高屬性值,這個屬性值就是要加載的圖片的寬高,順便提一句,Glide框架會根據請求加載圖片的ImageView的寬高來進行加載相對應的寬高圖片,每次根據view的大小加載的圖片是不必定同樣的。最後調用engine中的load方法,咱們再來看看load方法的源碼:
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, Options options, boolean isMemoryCacheable, boolean useUnlimitedSourceExecutorPool, boolean onlyRetrieveFromCache, ResourceCallback cb) { Util.assertMainThread(); long startTime = LogTime.getLogTime(); EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options); EngineResource<?> cached = loadFromCache(key, isMemoryCacheable); if (cached != null) { cb.onResourceReady(cached, DataSource.MEMORY_CACHE); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Loaded resource from cache", startTime, key); } return null; } EngineResource<?> active = 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; } EngineJob<?> current = jobs.get(key); if (current != null) { current.addCallback(cb); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Added to existing load", startTime, key); } return new LoadStatus(cb, current); } EngineJob<R> engineJob = engineJobFactory.build(key, isMemoryCacheable, useUnlimitedSourceExecutorPool); DecodeJob<R> decodeJob = decodeJobFactory.build( glideContext, model, key, signature, width, height, resourceClass, transcodeClass, priority, diskCacheStrategy, transformations, isTransformationRequired, onlyRetrieveFromCache, options, engineJob); jobs.put(key, engineJob); engineJob.addCallback(cb); engineJob.start(decodeJob); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Started new load", startTime, key); } return new LoadStatus(cb, engineJob); }
哇吼,全部重要的東西都在這裏了,解析下:
①:判斷是否在主線程運行,說明到目前爲止仍是在主線程執行的,並無真正的開啓子線程。
②:經過keyFactory工廠來構建一個EngineKey對象,key關聯着model,也就是url,它很根據model,view的寬高等等屬性來構建一個EngineKey對象,這個對象能夠用來指定緩存地址,能夠用來從緩存中查找資源等。
③:根據建立的key對象分別調用loadFromCache和loadFromActiveResources方法來從內存中查找是否有緩存資源,若是有,則回調cb.onResourceReady來直接設置圖片了。
④:分別使用engineJobFactory和decodeJobFactory構建EngineJob和DecodeJob對象,這兩個對象是真正的加載資源的兩個重要類,EngineJob對象負責開啓線程去加載資源,而且加載得資源後轉換到主線程並進行回調;DecodeJob是真正的執行者,它就是去網絡加載資源的地方,EngineJob開啓線程,真正執行的是DecodeJob,DecodeJob以後完畢以後叫道EngineJob去分發回調。這就是這兩個類的關係。
⑤:EngineJob和DecodeJob的構建是基本一致的,咱們看看比較複雜的DecodeJob的構建:在build方法中,首先經過pool來建立一個DecodeJob對象,而後調用DecodeJob對象的init方法進行初始化,在初始化中值得注意的是調用了decodeHelper對象的init方法。decodeHelper方法是DecodeJob的重要輔助類,後面咱們會詳細的接觸它。
⑥:上面也提到回調,這裏先cb添加到engineJob.addCallback();中,而後調用EngineJob的start方法來開啓線程。
咱們再來看看start方法中的源碼:
start方法中將會調用GlideExecutor的execute方法:
execute中正式的開啓了線程池進行加載資源。由此咱們也正式的由主線程轉到了子線程中。
上面咱們也分析了,真正執行線程的是在DecodeJob類中,那麼咱們去看看的run方法是怎麼執行的:
run方法中調用runWrapped方法,主要就是在它裏面執行的,來看看它的源碼:
咱們知道,在構造DecodeJob時調用init方法是runReason被賦值爲INITIALIZE值,由此它將會進入到INITIALIZE分支中,調用getNextStage方法:
在getNextStage方法中經錯幾回的經轉會返回Stage.SOURCE值,而後在調用getNextGenerator方法來獲取當前的currentGenerator對象,咱們在來看看獲取的這個currentGenerator究竟是什麼?
getNextGenerator方法中根據stage值來建立對象,由此咱們能夠知道currentGenerator是一個SourceGenerator對象,那麼咱們繼續往下走,來看看runGenerators方法:
runGenerators最重要的就是執行了currentGenerator的startNext方法,這裏將會真正的去加載網絡資源:
咱們從上面知道currentGenerator就是SourceGenerator對象,那麼咱們去看看SourceGenerator中startNext的實現:
到這裏就太關鍵了,由於不少人都死在這一步上,根本找不到是怎麼去加載資源的,是怎麼執行到咱們熟悉的HttpURLConnection的,那麼到這裏你千萬不能分神,稍微分神就會轉迷糊了,哈哈,那麼接下來就開始分析吧:
因爲咱們在建立SourceGenerator對象時,只是傳遞了DecodeHelper和回調cb對象,其餘的一切初始化操做都是不存在的,因此在startNext中,前面的兩個判斷是不成立的,主要是看while循環裏面的內容:
while循環中首先調用hasNextModelLoader進行判斷,咱們來看下hasNextModelLoader的內容,這裏必須集中精力理解清楚,否則確定不知所云:
private boolean hasNextModelLoader() { return loadDataListIndex < helper.getLoadData().size(); }
在hasNextModelLoader中它要去helper加載數據,調用getLoadData方法,咱們跟進去看看:
在getLoadData方法中是調用GlideContext中的getRegistry方法來回去Registry對象,它是在Glide的構造方法中建立的,並且註冊添加了不少解析器還記得嗎?不記得的趕快去看看Glide的構造方法。這裏咱們插入一點很是重要的分析,理解清楚它對接下來的網絡執行很是的重要:回到Glidet的構造方法來看看Registry中幾個特殊的解析類:
registry.register(xx) .append(xx) ... .append(Uri.class, InputStream.class, new HttpUriLoader.Factory()) ... .append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory()) ... ;
還有印象這段很長的代碼吧?
咱們重點來看看上面的兩個append方法,其餘的都是同樣或是相似的道理的。跟進去registry的append方法中:
繼續進入modelLoaderRegistry.append方法中:
再跟進multiModelLoaderFactory.append方法中:
重點來了,最後進去add方法:
這裏能夠看到什麼?你理解了嗎?
建立了一個Entry對象,把咱們的modelClass,dataClass和factory對象關聯起來,而後存放到entries的list集合中,就這麼簡單,可是對這個Entry對象的理解關係到咱們後面對整個網絡加載的流程十分的巨大,ok,到這裏,咱們插入的講解已經完了,主要想告訴你的就是這個entries集合包含了那些對象和建立Entry對象所關聯的類和工廠。
那麼如今回到DecodeHelper中的getLoadData方法中,它從GlideContext獲取到Registry對象,Registry對象有哪些內容在上面的插入講解中也已舉特例分析了,而後調用getModelLoaders方法,並傳進model對象,那麼來看看它是怎麼實現的:
它會從modelLoaderRegistry中獲取,在來看:
它會經過model從getModelLoadersForClass方法中獲取到modelLoaders的集合,來看看:
首先從cache緩存中獲取,若是爲空,將會從multiModelLoaderFactory工廠中獲取,在繼續跟進multiModelLoaderFactory的build方法看看:
從entries集合中分別的遍歷出entry對象,而後調用entry.handles來進行匹配是否符合,來看handles方法:
this.modelClass是什麼還記得嗎?沒錯就是在Glide建立Registry對象是append的XX.class,例如:
.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
而modelClass呢,這裏就是咱們傳遞用來加載網絡圖片的一個url地址,那麼調用isAssignableFrom方法進行匹配,咱們知道entries包含不少的解析器,因此在這一步將會排除掉不匹配的解析器,而後調用調用build方法來建立加載器:
entry.factory知道是什麼吧?沒錯就是append方法中new的一個工廠類。
仍是如下面的特例來講:
.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
在這裏這個factory就是HttpUriLoader.Factory,咱們來看看它是怎麼實現的:
這裏它將會multiFactory.build方法來構造一個ModelLoader對象,看清楚它傳遞的參數:GlideUrl.class, InputStream.class,而後跟進去查看:
歷史老是驚人的類似,再次從entries中獲取Entry對象,而後調用entry.handles方法根據GlideUrl.class, InputStream.class這兩個參數進行匹配過濾。
而後咱們找到了如下的append內容相匹配的Entry對象
.append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
找到HttpGlideUrlLoader.Factory以後,而後調用build方法去構建,在build方法中一樣的方式調用entry.factory.build(this),來看看HttpGlideUrlLoader.Factory()的build的源碼:
直接的建立一個HttpGlideUrlLoader對象並返回。
到此咱們獲取了真正的圖片加載對象,而後咱們回到HttpUriLoader的Factory中:在multiFactory.build(GlideUrl.class, InputStream.class)獲取到HttpGlideUrlLoader對象後,並傳遞到建立的HttpUriLoader對象中去,咱們來看看:
把HttpGlideUrlLoader對象賦值給HttpUriLoader的成員變量this.urlLoader中。
ok,到此咱們真正的加載器已經獲取到了,固然並非只有一個,可能有多個,由於在Registry註冊了多個能夠解析Uri.class的解析器。
好,咱們在回到ModelLoaderRegistry類中的getModelLoaders方法中,從getModelLoadersForClass方法中咱們獲取到了能夠解析咱們請求modle的全部解析器,經過for循環遍歷出全部的解析器,存放到filteredLoaders集合中並返回,一直返回到DecodeHelper類中的getLoadData方法中。
而後遍歷modelLoaders集合,分別獲取ModelLoader對象,並調用buildLoadData方法,咱們知道modelLoaders集合中必定會包含一個ModelLoader是HttpUriLoader,那來看看它的buildLoadData方法:
它會調用urlLoader.buildLoadData方法,這個urlLoader就是HttpGlideUrlLoader對象,再來看看:
這裏最重要的就是建立個一個HttpUrlFetcher對象:
而後把HttpUrlFetcher對象存放到新建的LoadData對象中:
最後把LoadData對象返回,咱們往上返回到SourceGenerator的startNext方法中:
在獲取到LoadData對象後,調用loadData.fetcher.loadData(helper.getPriority(), this);這個方法,從上面分析loadData.fetcher就是HttpUrlFetcher對象,那咱們來看看它裏面的loadData是怎麼加載數據的:
它會調用loadDataWithRedirects方法來返回一個InputStream輸入流,來看看loadDataWithRedirects的源碼:
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. } } urlConnection = connectionFactory.build(url); for (Map.Entry<String, String> headerEntry : headers.entrySet()) { urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue()); } urlConnection.setConnectTimeout(timeout); urlConnection.setReadTimeout(timeout); urlConnection.setUseCaches(false); urlConnection.setDoInput(true); // 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. urlConnection.connect(); if (isCancelled) { return null; } final int statusCode = urlConnection.getResponseCode(); if (statusCode / 100 == 2) { return getStreamForSuccessfulRequest(urlConnection); } else if (statusCode / 100 == 3) { String redirectUrlString = urlConnection.getHeaderField("Location"); if (TextUtils.isEmpty(redirectUrlString)) { throw new HttpException("Received empty or null redirect url"); } URL redirectUrl = new URL(url, redirectUrlString); return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers); } else if (statusCode == -1) { throw new HttpException(statusCode); } else { throw new HttpException(urlConnection.getResponseMessage(), statusCode); } }
這裏的代碼咱們就很是的熟悉了,稍微的簡單介紹下:
①:connectionFactory對象是在建立HttpUrlFetcher對象時在構造方法中初始化的,它就是DEFAULT_CONNECTION_FACTORY,也就是DefaultHttpUrlConnectionFactory工廠類,當調用它的build方法時:
經過url打開鏈接,返回一個HttpURLConnection對象,用於網絡請求,這些代碼日常咱們都有接觸,很少說。
②:創建鏈接,過去返回狀態碼,判斷,而後經過getStreamForSuccessfulRequest方法返回一個InputStream輸入流。
在獲取到InputStream輸入流以後,最會將會調用callback.onDataReady(result);回調方法,並把輸入流傳遞過去。
那這個callback是什麼呢?
還記得loadData.fetcher.loadData(helper.getPriority(), this);這段代碼吧?
沒錯他就是SourceGenerator類實現的DataCallback回調類。
那麼在進到SourceGenerator找到onDataReady方法吧:
在這裏它又調用cb回調類的onDataFetcherReady方法,並傳遞了相關參數:loadData.fetcher是HttpUrlFetcher,loadData.fetcher.getDataSource()則是DataSource.REMOTE:
@Override public DataSource getDataSource() { return DataSource.REMOTE; }
那麼這個cb又是什麼呢,它是FetcherReadyCallback回調類,在DecodeJob中實現,那麼回到DecodeJob的onDataFetcherReady方法中:
在onDataFetcherReady方法中保存了相關的參數變量,判斷是不是當前線程,而後調用decodeFromRetrievedData方法來解碼數據:
首先建立一個Resource類型的變量,經過decodeFromData方法把輸入流解碼並返回給resource,由此可也看出,解碼主要是在decodeFromData方法中:
在這裏已經體現loadData.fetcher這個fetcher的用意,主要是去關閉輸入流和HttpUrlConnection的。
@Override public void cleanup() { if (stream != null) { try { stream.close(); } catch (IOException e) { // Ignore } } if (urlConnection != null) { urlConnection.disconnect(); } }
在這以前它又調用decodeFromFetcher方法來進行解碼返回一個Resource:
在這裏獲取到data.getClass,這個Class就是InputStrem.class,那麼在調用decodeHelper.getLoadPath方法後,咱們來看看作了哪些操做:
getLoadPath方法啥也沒作,直接調用Registry中的getLoadPath,並傳遞了擁有的變量,其中resourceClass是Object.class,transcodeClass則是Drawable.class,這是在前面已構建初始化好的,直接拿來用。
咱們在來跟進去看看
這裏首先從loadPathCache緩存中獲取LoadPath對象,若是沒有則調用getDecodePaths方法進行獲取:
getDecodePaths方法中仍是蠻重要的,對真個解碼過程的理解有很大的幫助,因此咱們來認真的分析下:
①:首先從decoderRegistry.getResourceClasses方法中獲取已經註冊的registeredResourceClasses:
還記得咱們建立Registry是註冊了一大堆的東西嗎?
對,經過handles方法對InputStrem.class和Object.class進行匹配的就是咱們用紅色框畫出來的部分。由此咱們能夠得出registeredResourceClasses集合中分別對應的是Bitmap.class,BitmapDrawable.class和GifDrawable.class的三種Class對象。
②:遍歷registeredResourceClasses集合,經過transcoderRegistry.getTranscodeClasses方法獲取到已註冊的registeredTranscodeClasses集合:
這是對registeredResourceClasses集合的再次匹配,咱們知道transcodeClasses其實是Drawable.class,在registeredResourceClasses集合中只有BitmapDrawable.class和GifDrawable.class是繼承Drawable的,由此咱們得出registeredTranscodeClasses包含BitmapDrawable.class和GifDrawable.class兩種Class對象。
那麼由此可知decoders存放了兩種解碼器:
對應的BitmapDrawable的BitmapDrawableDecoder解碼器。
對應的GifDrawable.class的StreamGifDecoder解碼器。
這個很是重要,在後面有使用到。必須理解清楚。
③:經過上面獲取的registeredResourceClass和registeredTranscodeClass獲取到transcoder轉化器,主要是從transcoderRegistry集合中經過get方法獲取,而transcoderRegistry又包含哪些轉化器呢?
而又有get方法咱們能夠知道:
當時普通圖片是transcoder將會是BitmapDrawableTranscoder,如是過動態圖片gif的話transcoder將會是GifDrawableBytesTranscoder。
而後把它們存放到建立的DecodePath對象中:
最後是把封裝好的DecodePath對象存儲到decodePaths集合中並返回。
而後在回到Registry中的getLoadPath方法中,在獲取到decodePaths集合中,把它和dataClass, resourceClass, transcodeClass又封裝到LoadPath對象中,並緩存到loadPathCache對象中。
再次返回DecodeJob類中的decodeFromFetcher方法中,在獲取到LoadPath後,調用runLoadPath方法,來看下它的源碼:
它調用glideContext.getRegistry()獲取Registry對象,而後調用Registry的getRewinder獲取裝置器,跟進去看看:
在getRewinder方法中直接的調用dataRewinderRegistry對象的build方法,那麼這個dataRewinderRegistry又是什麼呢?
它是在Registry構造方法中建立的對象:
this.dataRewinderRegistry = new DataRewinderRegistry();
並且在Glide中進行了註冊:
獲取到factory以後調用dataRewinderRegistry.register方法註冊到Map集合rewinders中去:
再來看看factory.getDataClass()都獲取到哪些key:
①:在ByteBufferRewinder.Factory中調用getDataClass()獲取的key是ByteBuffer.class
②:在InputStreamRewinder.Factory中調用getDataClass()獲取的key是InputStream.class
上面兩個key值在接下來會使用到。
那麼咱們在返回到getRewinder方法中,調用dataRewinderRegistry對象的build方法到底作了什麼:
在build的方法中總共作了四件事,分別用不一樣的線框標註出來了:
①:從rewinders集合中獲取到和data.getClass()相匹配的Factory對象,從上面的分析中,咱們知道rewinders註冊的只有兩個key,分別是ByteBuffer.class和InputStream.class,而咱們又知道data.getClass()是一個InputStream.class,由此能夠匹配成功。這個result就是InputStreamRewinder.Factory對象。
②:假如result沒有匹配成功的話,也就是沒有經過key匹配成功,那麼就進行遍歷rewinders集合,經過values值進行匹配,把匹配成功的Factory對象再賦值給result。
③:假如經過鍵key和值values都沒有匹配成功,那麼也沒關係,直接使用默認的DEFAULT_FACTORY
④:最後調用result的build方法。
這裏因爲咱們經過鍵key直接已匹配成功,因此咱們知道result就是InputStreamRewinder.Factory對象,那麼來看看調用它的build方法作了什麼事情:
這裏直接的返回了一個InputStreamRewinder對象,在InputStreamRewinder的構造方法中又建立了RecyclableBufferedInputStream對象,並它InputStream流傳遞進去:
RecyclableBufferedInputStream繼承FilterInputStream流,那麼咱們的InputStream流最終保存的位置就是在FilterInputStream類中。
ok,到這裏咱們知道了InputStream數據流的去向,而在InputStreamRewinder中又有對InputStream流的引用,那麼在回到InputStreamRewinder以後咱們來看看接下來又作了什麼呢?
再次定位到DecodeJob類中的runLoadPath方法,在獲取到InputStreamRewinder後,它會調用path.load方法並返回Resource資源類,這個path是在以前獲取到的LoadPath對象,這裏注意下,傳遞了一個new DecodeCallback 對象的回調類,後面會用到。最後調用InputStreamRewinder.cleanup()進行資源釋放。那麼咱們來看看LoadPath的load方法是怎麼實現的:
它直接調用loadWithExceptionList方法進行移交:
在loadWithExceptionList方法中則會調用decode方法進行解碼:
decode方法中調用decodeResource方法,而後在調用decodeResourceWithList方法真正的開始解碼:
①:這裏首先遍歷decoders集合,分別的獲取到ResourceDecoder解碼器,還記得咱們的decoders都包含哪些解碼器嗎?沒錯主要包含兩種:BitmapDrawable.class和GifDrawable.class。不記得的往上面翻下,上面已詳細的講解過了。
②:而後經過rewinder.rewindAndGet()獲取咱們的InputStream數據流:
③:經過decoder.handles(data, options)方法來過濾掉不相匹配的解碼器,再來看看前面的這張圖:
Bitmap.class已被咱們過濾掉,剩下的就只有BitmapDrawable.class和GifDrawable.class,當咱們調用handles方法進行匹配時,StreamGifDecoder解碼器是怎麼處理的:
它主要是針對options屬性爲gif的圖片來解碼的,其實這不用我說你們也是知道的,而咱們加載的只是普通的靜態圖片,所以它是不符合咱們的匹配規則的,在來看BitmapDrawableDecoder中,它傳入的是StreamBitmapDecoder的解碼器:
而downsampler.handles(source);中只要source是InputStream就返回true,所以匹配成功。
那麼由上分析,咱們真正的decoder就是BitmapDrawableDecoder。
回到decodeResourceWithList方法中,獲取到真正的decoder解碼器,將會調用decode方法正式解碼:
它又會交給StreamBitmapDecoder的decode方法,而後在轉移到Downsampler類中的decode方法中:
這裏就再也不一個一個的看方法了,主要是在Downsampler中decodeFromWrappedStreams方法中把InputStream數據流根據width, height, options給轉化成Bitmap圖片,而後把Bitmap存放到BitmapResource對象中去且直接的返回自己。
ok,獲取到BitmapResource對象後,再次返回最初開始解碼的DecodePath類中的decode方法中:
從上面分析咱們知道,咱們解析的是普通的圖片,因此這個transcoder就是BitmapDrawableTranscoder轉換器類。(前面有詳細的分析,忘記的能夠向上翻看)
接着咱們去BitmapDrawableTranscoder中的transcode方法看看:
在transcode方法中經過toTranscode.get()獲取bitmap圖片,這個bitmap雖然通過多層的包裝,它其實就是一個BitmapResource對象,這在上面咱們有清楚的分析,只不過在返回的回調方法中有屢次的封裝而已。
獲取到Bitmap圖片後,調用LazyBitmapDrawableResource的obtain方法再次進行一次的封裝:
把Bitmap封裝到LazyBitmapDrawableResource對象中進行返回。
ok,到這裏其實已完成了完徹底全的解碼和封裝到。
一路把LazyBitmapDrawableResource對象返回到DecodeJob類中的decodeFromRetrievedData中,並賦值給resource變量:
當resource不爲空時,將會調用notifyEncodeAndRelease方法並傳遞參數:
方法裏面就很少解析了,它會調用notifyComplete方法:
這裏調用了callback.onResourceReady(resource, dataSource);方法,那這個callback是什麼呢?它實際上是一個ResourceCallback,在SingleRequest中發起的,而且SingleRequest還實現了ResourceCallback接口內的方法:
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())) { releaseResource(resource); GlideException exception = new GlideException("Expected to receive an object of " + transcodeClass + " but instead" + " got " + (received != null ? received.getClass() : "") + "{" + received + "} inside" + " " + "Resource{" + resource + "}." + (received != null ? "" : " " + "To indicate failure return a null Resource " + "object, rather than a Resource object containing null data.")); onLoadFailed(exception); return; } if (!canSetResource()) { releaseResource(resource); // We can't put the status to complete before asking canSetResource(). status = Status.COMPLETE; return; } onResourceReady((Resource<R>) resource, (R) received, dataSource); }
重點講解下:在onResourceReady中使用resource.get(),咱們知道這個resource就是LazyBitmapDrawableResource對象,來看看這個get獲取到了什麼:
在get中獲取到了BitmapDrawable對象直接複製給了received變量,而後調用重載方法onResourceReady方法:
在onResourceReady方法中調用了target.onResourceReady(result, animation);還記得target是什麼嗎?
它在load方法中已講解過,就是DrawableImageViewTarget對象,調用它的onResourceReady會轉移到父類ImageViewTarget中:
而後在調用setResourceInternal方法:
setResource是抽象方法,由它的子類實現,咱們在回到DrawableImageViewTarget中:
到這裏直接調用view進行設置圖片了,這個view就是咱們Imageview了,因此到這裏就設置好了加載的圖片了。
ok,到這裏就完徹底全的解析完了Glide的執行原理了。
說實在的Glide的源碼很是的複雜,每每深刻進去就沒法出來了,相信這篇博客很能好的給你們一個參考,能讓你們對整個Glide有個全面的理解。
好了,終於寫完了,真的很不容易,很辛苦,但願你們也能從中學到知識。
各位若是還有哪裏不明白的,或是我這裏講的還不夠透徹,亦或是講錯了的地方請留言指正,讓咱們共同進步,謝謝
同時,請你們掃一掃關注個人微信公衆號,雖然寫的不是很勤,可是每一篇都有質量保證,讓您學習到真正的知識。