本文基於Glide 4.11.0
Glide載入過程有一個解碼過程,比如將url載入為inputStream後,要將inputStream解碼為Bitmap。
從Glide原始碼解析一我們大致知道了Glide載入的過程,所以我們可以直接從這裡看起,在這個過程中我們以從檔案中載入bitmap為例:
DecodeJob的一個方法:
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(); } }
主要是這個方法:resource = decodeFromData(currentFetcher, currentData, currentDataSource);
這時候currentData為FileInputStream,因為我們載入的是本地檔案。
currentDateSource為LOCAL,即為本地的資源
我們繼續找下去
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
----------------->
Resource<R> result = decodeFromFetcher(data, dataSource);
------------------>
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource) throws GlideException { LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass()); return runLoadPath(data, dataSource, path);
這裡獲取到LoadPath的物件,我麼先看看LoadPath有什麼?
我們可以看到一個DecodePaths:
DecodePath裡面又儲存著decoders
decoders便是我們需要的解碼器,拿到解碼器後就可以進行解碼了。
那怎麼拿到?
在Glide原始碼解析三中我們知道這些解碼器都註冊在Register中,所以我們也是要透過它來拿:
<Data> LoadPath<Data, ?, Transcode> getLoadPath(Class<Data> dataClass) { return glideContext.getRegistry().getLoadPath(dataClass, resourceClass, transcodeClass); }
---------------->
@Nullable public <Data, TResource, Transcode> LoadPath<Data, TResource, Transcode> getLoadPath( @NonNull Class<Data> dataClass, @NonNull Class<TResource> resourceClass, @NonNull Class<Transcode> transcodeClass) { LoadPath<Data, TResource, Transcode> result = loadPathCache.get(dataClass, resourceClass, transcodeClass); if (loadPathCache.isEmptyLoadPath(result)) { return null; } else if (result == null) { List<DecodePath<Data, TResource, Transcode>> decodePaths = getDecodePaths(dataClass, resourceClass, transcodeClass); // It's possible there is no way to decode or transcode to the desired types from a given // data class. if (decodePaths.isEmpty()) { result = null; } else { result = new LoadPath<>( dataClass, resourceClass, transcodeClass, decodePaths, throwableListPool); } loadPathCache.put(dataClass, resourceClass, transcodeClass, result); } return result; }
首先會先從快取中拿,快取中拿不到再透過下面的方法去拿:
List<DecodePath<Data, TResource, Transcode>> decodePaths = getDecodePaths(dataClass, resourceClass, transcodeClass);
private <Data, TResource, Transcode> List<DecodePath<Data, TResource, Transcode>> getDecodePaths( @NonNull Class<Data> dataClass, @NonNull Class<TResource> resourceClass, @NonNull Class<Transcode> transcodeClass) { List<DecodePath<Data, TResource, Transcode>> decodePaths = new ArrayList<>(); List<Class<TResource>> registeredResourceClasses = decoderRegistry.getResourceClasses(dataClass, resourceClass); for (Class<TResource> registeredResourceClass : registeredResourceClasses) { List<Class<Transcode>> registeredTranscodeClasses = transcoderRegistry.getTranscodeClasses(registeredResourceClass, transcodeClass); for (Class<Transcode> registeredTranscodeClass : registeredTranscodeClasses) { List<ResourceDecoder<Data, TResource>> decoders = decoderRegistry.getDecoders(dataClass, registeredResourceClass); ResourceTranscoder<TResource, Transcode> transcoder = transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass); @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") DecodePath<Data, TResource, Transcode> path = new DecodePath<>(dataClass, registeredResourceClass, registeredTranscodeClass, decoders, transcoder, throwableListPool); decodePaths.add(path); } } return decodePaths; }
該方法各個引數如下:
dataClass為InputStream,這是被解碼的物件
resourceClass為Object,要解碼成為Object
transcodeClass為Drawable,要轉碼為Drawable
我們看這個方法:
decoderRegistry.getResourceClasses:
public synchronized <T, R> List<Class<R>> getResourceClasses(@NonNull Class<T> dataClass, @NonNull Class<R> resourceClass) { List<Class<R>> result = new ArrayList<>(); for (String bucket : bucketPriorityList) { List<Entry<?, ?>> entries = decoders.get(bucket); if (entries == null) { continue; } for (Entry<?, ?> entry : entries) { if (entry.handles(dataClass, resourceClass) && !result.contains((Class<R>) entry.resourceClass)) { result.add((Class<R>) entry.resourceClass); } } } return result; }
該方法是為了獲取解碼器中的resourceClass,即解碼後的資源型別。
我們可以看到decoder這個map裡面的內容:
各種型別對應的解碼器。
只有滿足entry.handles(dataClass, resourceClass),才能被新增返回:
public boolean handles(@NonNull Class<?> dataClass, @NonNull Class<?> resourceClass) { return this.dataClass.isAssignableFrom(dataClass) && resourceClass .isAssignableFrom(this.resourceClass); }
由於我們的resourceClass是Object,因此resourceClass .isAssignableFrom(this.resourceClass)總是成立的,所以就看:this.dataClass.isAssignableFrom(dataClass)
而我們的dataClass是InputStream,開啟各種型別,可以看到哪些的dataClass是InputStream:
上面框錯了,應該框resourceClass,另外FrameSequenceDrawable是我自定義後註冊進去的,所以Glide原生的是沒有的。
所以最終返回的resource為:
接下來是針對每一種resourceClass獲取對應的轉碼類(要轉成的物件):
public synchronized <Z, R> List<Class<R>> getTranscodeClasses( @NonNull Class<Z> resourceClass, @NonNull Class<R> transcodeClass) { List<Class<R>> transcodeClasses = new ArrayList<>(); // GifDrawable -> Drawable is just the UnitTranscoder, as is GifDrawable -> GifDrawable. if (transcodeClass.isAssignableFrom(resourceClass)) { transcodeClasses.add(transcodeClass); return transcodeClasses; } for (Entry<?, ?> entry : transcoders) { if (entry.handles(resourceClass, transcodeClass)) { transcodeClasses.add(transcodeClass); } } return transcodeClasses; }
如果transcodeClass是resourceClass的父類那就直接返回。
第一個GifDrawable,返回的registeredTranscodeClasses為:
然後根據dataClass, registeredResourceClass獲取decoders:
然後根據registeredResourceClass和registeredTranscodeClass獲取transcoder
上面具體的獲取過程是類似的,就不過多分析了。
然後構造DecodePath,放進下面的集合裡面:
List<DecodePath<Data, TResource, Transcode>> decodePaths = new ArrayList<>();
迴圈獲取之後,最終得到的decodePaths如下:
大致流程:
1、先根據傳進來的resourceClass獲取登錄檔中所有註冊的resourceClass得到List<Class<TResource>> registeredResourceClasses
2、兩層for迴圈:
(1)外層:根據registeredResourceClasses獲取轉碼的class :List<Class<Transcode>> registeredTranscodeClasses
(2)內層:
a、根據資源resourceClass獲取所有的解碼器。
b、根據資源resourceClass和轉碼transcodeClass獲取所有的轉碼器。
c、構造DecodePath,放進集合裡面。
最後得到的List<DecodePath<Data, TResource, Transcode>> decodePaths被放到LoadPath物件裡面(上一層方法可看到)
我們又回到DecodeJob中的方法:
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource) throws GlideException { LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass()); return runLoadPath(data, dataSource, path); }
獲取到LoadPath後接下來就是要開始執行了runLoadPath了。
找下去可以看到該方法:
return path.load( rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
該方法屬於LoadPath物件。
層層追溯後,最終來到下面的方法:
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; } } if (result == null) { throw new GlideException(failureMessage, new ArrayList<>(exceptions)); } return result; }
該方法在LoadPath裡面,遍歷decodePaths(這是我們之前獲取後放在LoadPath中的)進行解碼:
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:
最終來到DecodePath裡面的方法:
@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; }
這個方法:decoder.handles(data, options)是判斷該解碼器是否可以對該資源進行解碼,這個方法寫在每個解碼器裡面。
DataRewinder裡面放著需要進行解碼的資料。
解碼後將資源返回。
又回到這個方法:
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);
是對資源進行變換處理,比如圖片的縮放,剪裁等等,這個功能單獨拎出來講。
接下來便是運用轉碼器進行資源的轉碼:
transcoder.transcode(transformed, options)
到此就結束了。