系列文章:
接着上篇筆記,按照Glide的流程查詢完內存緩存之後應該算是查詢磁盤緩存,但是由於磁盤緩存的數據依賴第一次請求的時候從網絡下載,再寫入磁盤緩存。所以我這篇先講網絡請求部分的流程。
這部分的代碼坦白講比較多也比較繞,我在看的時候也看的頭暈。這裏就不去把代碼都列出來了,感興趣的同學可以跟着下面的時序圖去追蹤一下:
EngineJob的功能比較簡單,就是管理加載和回調:
/**
* A class that manages a load by adding and removing callbacks for for the load and notifying
* callbacks when the load completes.
*/
class EngineJob<R> implements DecodeJob.Callback<R>, Poolable {
...
}
它最重要的功能是啓動了DecodeJob去加載資源,DecodeJob是一個運行在子線程的Runnable。它會使用Generator去從緩存或者數據源加載數據,我們這次只看從數據源加載的SourceGenerator。
SourceGenerator在啓動的時候會使用DecodeHelper去獲取一個叫LoadData的東西,這一步比較有意思我們展開講,先看看DecodeHelper.getLoadData的代碼:
List<LoadData<?>> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
for (int i = 0, size = modelLoaders.size(); i < size; i++) {
ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
LoadData<?> current = modelLoader.buildLoadData(model, width, height, options);
if (current != null) {
loadData.add(current);
}
}
}
return loadData;
}
loadData是個List,而且DecodeHelper內部做了緩存。它的加載邏輯是先用model(就是我們load傳入的url)從glideContext裏面查詢ModelLoader列表,然後遍歷它去buildLoadData丟到loadData這個列表裏面。
ModelLoader的查詢與註冊
getModelLoaders的邏輯很簡單,就用modelLoaderRegistry去getModelLoaders:
@NonNull
public <Model> List<ModelLoader<Model, ?>> getModelLoaders(@NonNull Model model) {
return modelLoaderRegistry.getModelLoaders(model);
}
modelLoaderRegistry裏面會根據model的class查詢modelLoaders列表,然後遍歷它去使用ModelLoader.handles方法判斷這個ModelLoader是否支持這個model,如果是的再放到filteredLoaders裏面一起返回。
public <A> List<ModelLoader<A, ?>> getModelLoaders(@NonNull A model) {
List<ModelLoader<A, ?>> modelLoaders = getModelLoadersForClass(getClass(model));
...
int size = modelLoaders.size();
boolean isEmpty = true;
List<ModelLoader<A, ?>> filteredLoaders = Collections.emptyList();
for (int i = 0; i < size; i++) {
ModelLoader<A, ?> loader = modelLoaders.get(i);
if (loader.handles(model)) {
if (isEmpty) {
filteredLoaders = new ArrayList<>(size - i);
isEmpty = false;
}
filteredLoaders.add(loader);
}
}
...
return filteredLoaders;
}
這個邏輯要怎麼理解呢?舉個例子,就是假設model是一個Uri,那麼getModelLoadersForClass查出來的ModelLoader列表裏面可能有加載本地圖片的也可能有加載網絡圖片的。然後分別調用ModelLoader.handles方法去過濾。如果是個本地請求的Uri,網絡請求的ModelLoader就會被過濾掉,因爲它只支持http和https的scheme:
public class UrlUriLoader<Data> implements ModelLoader<Uri, Data> {
private static final Set<String> SCHEMES =
Collections.unmodifiableSet(new HashSet<>(Arrays.asList("http", "https")));
...
@Override
public boolean handles(@NonNull Uri uri) {
return SCHEMES.contains(uri.getScheme());
}
...
}
getModelLoadersForClass方法裏面也比較繞,我就不展開代碼了,只要知道它是根據model的class去查詢即可。
有了查詢就肯定有註冊,它的註冊在Glide的構造函數裏面有很長的一坨,第一個參數就是用於對比model class的,第二個參數是DataFetcher的數據類型,最後一個參數是ModelLoader的工廠:
registry
...
.append(String.class, InputStream.class, new StringLoader.StreamFactory())
...
.append(Uri.class, InputStream.class, new UriLoader.StreamFactory(contentResolver))
.append(Uri.class,ParcelFileDescriptor.class,new UriLoader.FileDescriptorFactory(contentResolver))
.append(Uri.class,AssetFileDescriptor.class,new UriLoader.AssetFileDescriptorFactory(contentResolver))
.append(Uri.class, InputStream.class, new UrlUriLoader.StreamFactory())
.append(URL.class, InputStream.class, new UrlLoader.StreamFactory())
...
光看這個流程其實還比較好理解,但是由於我們使用的是String類型的url,當我第一次追蹤代碼的時候還是有被繞暈。原因是String.class註冊的StringLoader自己並不幹活,而是將String轉換成Uri再去registry裏面找人幹活:
public class StringLoader<Data> implements ModelLoader<String, Data> {
public StringLoader(ModelLoader<Uri, Data> uriLoader) {
this.uriLoader = uriLoader;
}
@Override
public LoadData<Data> buildLoadData(@NonNull String model, int width, int height, @NonNull Options options) {
Uri uri = parseUri(model);
if (uri == null || !uriLoader.handles(uri)) {
return null;
}
return uriLoader.buildLoadData(uri, width, height, options);
}
@Override
public boolean handles(@NonNull String model) {
return true;
}
...
public static class StreamFactory implements ModelLoaderFactory<String, InputStream> {
public ModelLoader<String, InputStream> build(@NonNull MultiModelLoaderFactory multiFactory) {
return new StringLoader<>(multiFactory.build(Uri.class, InputStream.class));
}
...
}
...
}
可以看到StreamFactory創建StringLoader的時候調用了multiFactory.build(Uri.class, InputStream.class)方法創建了一個ModelLoader傳給StringLoader。這個build出來的是一個MultiModelLoader,具體細節我也不講了。它從registry查詢了append時候第一個參數爲Uri.class,第二個參數爲InputStream.class的ModelLoader。
StringLoader.handles直接返回true,然後buildLoadData的時候在從這堆ModelLoader使用handles然後build出來。
也就是相當於將String.class的model轉換成了Uri.class類型。但是這樣還沒有完,Uri.class最終又被轉換成了GlideUrl.class,這個我就不展開代碼了...
DataFetcher
得到的ModelLoader回到到DecodeHelper.getLoadData去buildLoadData創建LoadData,就得到了一個LoadData列表。
然後SourceGenerator.startNext裏面就會遍歷這個列表去找到一個能加載資源的LoadData,其中主要幹活的是DataFetcher:
public boolean startNext() {
...
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;
}
startNextLoad裏面使用loadData.fetcher啓動資源的加載,完成後回調onDataReadyInternal:
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);
}
}
...);
}
但是值得注意的是Fetcher的加載完成並不是把圖片文件下載完成,只是打開了文件流而已,需要等待後面的流程從裏面讀取:
public class HttpUrlFetcher implements DataFetcher<InputStream> {
public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
...
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
callback.onDataReady(result);
...
}
}
LoadPath
看回時序圖可以知道,DataFetcher的數據是被LoadPath讀取去解碼的。這裏面也很複雜,但是我們並不需要全部瞭解,我這隻講個比較重要的東西。
LoadPath.load方法最終會調用到LoadPath.decodeResourceWithList方法。顧名思義它是遍歷解碼器列表查找一個解碼器去解碼文件:
private Resource<ResourceType> decodeResourceWithList(...) throws GlideException {
Resource<ResourceType> result = null;
for (int i = 0, size = decoders.size(); i < size; i++) {
ResourceDecoder<DataType, ResourceType> decoder = decoders.get(i);
...
DataType data = rewinder.rewindAndGet();
if (decoder.handles(data, options)) {
data = rewinder.rewindAndGet();
result = decoder.decode(data, width, height, options);
}
...
if (result != null) {
break;
}
}
...
return result;
}
這些解碼器是哪裏註冊的呢?答案還是Glide的構造函數裏面那坨很長的append,沒錯那坨append不僅註冊了ModelLoader還註冊瞭解碼器:
registry
.append(ByteBuffer.class, new ByteBufferEncoder())
.append(InputStream.class, new StreamEncoder(arrayPool))
.append(Registry.BUCKET_BITMAP, ByteBuffer.class, Bitmap.class, byteBufferBitmapDecoder)
.append(Registry.BUCKET_BITMAP, InputStream.class, Bitmap.class, streamBitmapDecoder)
...
總結
之後的流程就是不斷回到到Engine去放到弱引用緩存裏面的。到這裏整個網絡資源的下載解碼流程也就講完了,我們來看看簡化之後的時序圖: