DecodeHelper類相關方法分析
DecodeHelper類中,呼叫的方法,涉及到的東西比較多,最主要的是包括管理元件註冊以擴充套件或替換Glide的預設載入,解碼和編碼邏輯的Registry類。在Glide類的構造方法中,如下:
Glide(
@NonNull Context context,
@NonNull Engine engine,
@NonNull MemoryCache memoryCache,
@NonNull BitmapPool bitmapPool,
@NonNull ArrayPool arrayPool,
@NonNull RequestManagerRetriever requestManagerRetriever,
@NonNull ConnectivityMonitorFactory connectivityMonitorFactory,
int logLevel,
@NonNull RequestOptions defaultRequestOptions,
@NonNull Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions) {
....
registry
.append (ByteBuffer.class, new ByteBufferEncoder())
.append(InputStream.class, new StreamEncoder(arrayPool))
/* Bitmaps */
....
.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(String.class, InputStream.class, new StringLoader.StreamFactory())
.append(String.class, ParcelFileDescriptor.class, new StringLoader.FileDescriptorFactory())
.append(
String.class , AssetFileDescriptor.class, new StringLoader.AssetFileDescriptorFactory())
.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
.append(Uri.class, InputStream.class, new AssetUriLoader.StreamFactory(context.getAssets()))
.append(
Uri.class,
ParcelFileDescriptor.class,
new AssetUriLoader.FileDescriptorFactory(context.getAssets()))
.append(Uri.class, InputStream.class, new MediaStoreImageThumbLoader.Factory(context))
.append(Uri.class, InputStream.class, new MediaStoreVideoThumbLoader.Factory(context))
.append(
Uri.class,
InputStream.class,
new UriLoader.StreamFactory(contentResolver))
....
在這裡,註冊了一系列的資訊,我們這裡著手關注String.class這塊的ModelLoader物件。一般Glide載入一個url字串的時候,就是通過String.class取對應的ModelLoader物件。
.append(String.class, InputStream.class, new DataUrlLoader.StreamFactory<String>())
.append(String.class, InputStream.class, new StringLoader.StreamFactory())
.append(String.class, ParcelFileDescriptor.class, new StringLoader.FileDescriptorFactory())
.append(String.class, AssetFileDescriptor.class, new StringLoader.AssetFileDescriptorFactory())
最終就是往MultiModelLoaderFactory的成員變數entries加入了四條資料,Entry的結構如下:
private static class Entry<Model, Data> {
private final Class<Model> modelClass;
@Synthetic final Class<Data> dataClass;
@Synthetic final ModelLoaderFactory<? extends Model, ? extends Data> factory;
}
暫且先有點這個印象,註冊的過程不是我們要重點關注的,Registry不過只是一種構造ModelLoader的實現而已,下面我們先分析DecodeHelper的getLoadData的實現。
List<LoadData<?>> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
//noinspection ForLoopReplaceableByForEach to improve perf
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;
}
這裡的model是一個Object物件,對於我們的簡單url請求來說,它就是一個String類物件。通過程式碼追蹤,getModelLoaders方法,會返回一個ModelLoader的List,它的實現如下:
public <Model> List<ModelLoader<Model, ?>> getModelLoaders(@NonNull Model model) {
List<ModelLoader<Model, ?>> result = modelLoaderRegistry.getModelLoaders(model);
if (result.isEmpty()) {
throw new NoModelLoaderAvailableException(model);
}
return result;
}
繼續跟進ModelLoaderRegistry的getModelLoaders,
public synchronized <A> List<ModelLoader<A, ?>> getModelLoaders(@NonNull A model) {
List<ModelLoader<A, ?>> modelLoaders = getModelLoadersForClass(getClass(model));
int size = modelLoaders.size();
List<ModelLoader<A, ?>> filteredLoaders = new ArrayList<>(size);
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0; i < size; i++) {
ModelLoader<A, ?> loader = modelLoaders.get(i);
if (loader.handles(model)) {
filteredLoaders.add(loader);
}
}
return filteredLoaders;
}
先是呼叫取model的class型別,然後通過getModelLoadersForClass去獲取前面註冊進去的loaders資訊。獲取到之後,通過ModelLoader的handles方法進行一次過濾。最終返回,我們繼續分析getModelLoadersForClass。
private <A> List<ModelLoader<A, ?>> getModelLoadersForClass(@NonNull Class<A> modelClass) {
List<ModelLoader<A, ?>> loaders = cache.get(modelClass);
if (loaders == null) {
loaders = Collections.unmodifiableList(multiModelLoaderFactory.build(modelClass));
cache.put(modelClass, loaders);
}
return loaders;
}
可以看到,它是由multiModelLoaderFactory.build方法返回值決定的。下面繼續分析這個方法。
synchronized <Model> List<ModelLoader<Model, ?>> build(@NonNull Class<Model> modelClass) {
try {
List<ModelLoader<Model, ?>> loaders = new ArrayList<>();
for (Entry<?, ?> entry : entries) {
// Avoid stack overflow recursively creating model loaders by only creating loaders in
// recursive requests if they haven't been created earlier in the chain. For example:
// A Uri loader may translate to another model, which in turn may translate back to a Uri.
// The original Uri loader won't be provided to the intermediate model loader, although
// other Uri loaders will be.
if (alreadyUsedEntries.contains(entry)) {
continue;
}
if (entry.handles(modelClass)) {
alreadyUsedEntries.add(entry);
loaders.add(this.<Model, Object>build(entry));
alreadyUsedEntries.remove(entry);
}
}
return loaders;
} catch (Throwable t) {
alreadyUsedEntries.clear();
throw t;
}
}
Entry我們上面已經有所介紹,和String有關的目前有四個,重點就是entry.handles方法的返回值,決定其是否能加入到loaders中,而在這個方法中,實現就是看是否和modelClass能夠匹配,所以此時和String.class能匹配的就是上面已經列出的。
.append(String.class, InputStream.class, new DataUrlLoader.StreamFactory<String>())
.append(String.class, InputStream.class, new StringLoader.StreamFactory())
.append(String.class, ParcelFileDescriptor.class, new StringLoader.FileDescriptorFactory())
.append(String.class, AssetFileDescriptor.class, new StringLoader.AssetFileDescriptorFactory())
接下來會呼叫build方法傳入entry獲取一個ModelLoader物件,build實現如下:
private <Model, Data> ModelLoader<Model, Data> build(@NonNull Entry<?, ?> entry) {
return (ModelLoader<Model, Data>) Preconditions.checkNotNull(entry.factory.build(this));
}
這裡就是呼叫entry對應的factory物件去構建ModelLoader。這裡四個工廠分別是DataUrlLoader.StreamFactory、StringLoader.StreamFactory、StringLoader.FileDescriptorFactory與StringLoader.AssetFileDescriptorFactory。分別構造了DataUrlLoader、StringLoader、StringLoader、StringLoader四個loader物件。
對於一個url,這裡我們的是:”https://p.upyun.com/docs/cloud/demo.jpg“,ModelLoader中有一個handles方法,表明此型別是否可以被自己處理。
DataUrlLoader的實現如下:
public boolean handles(@NonNull Model model) {
// We expect Model to be a Uri or a String, both of which implement toString() efficiently. We
// should reconsider this implementation before adding any new Model types.
return model.toString().startsWith(DATA_SCHEME_IMAGE);
}
其中DATA_SCHEME_IMAGE為”data:image”,顯然此時不能處理我們的url。
StringLoader的實現如下:
public boolean handles(@NonNull String model) {
return true;
}
顯然此時,是可以處理這個url物件的。 三個Factory的build方法的實現如下:
public static class StreamFactory implements ModelLoaderFactory<String, InputStream> {
@NonNull
@Override
public ModelLoader<String, InputStream> build(MultiModelLoaderFactory multiFactory) {
return new StringLoader<>(multiFactory.build(Uri.class, InputStream.class));
}
}
/**
* Factory for loading {@link ParcelFileDescriptor}s from Strings.
*/
public static class FileDescriptorFactory
implements ModelLoaderFactory<String, ParcelFileDescriptor> {
@NonNull
@Override
public ModelLoader<String, ParcelFileDescriptor> build(MultiModelLoaderFactory multiFactory) {
return new StringLoader<>(multiFactory.build(Uri.class, ParcelFileDescriptor.class));
}
}
/**
* Loads {@link AssetFileDescriptor}s from Strings.
*/
public static final class AssetFileDescriptorFactory
implements ModelLoaderFactory<String, AssetFileDescriptor> {
@Override
public ModelLoader<String, AssetFileDescriptor> build(MultiModelLoaderFactory multiFactory) {
return new StringLoader<>(multiFactory.build(Uri.class, AssetFileDescriptor.class));
}
}
這塊相對有點複雜,在StringLoader中有一個成員變數uriLoader,它也是由entry傳入的MultiModelLoaderFactory的build方法繼續建立的。這裡我們可以看到有一個遞迴的過程,其實也很好理解,雖然三個都是StringLoader,但內部的成員變數uriLoader不一樣。分別是三對:
- Uri.class, InputStream.class
- Uri.class, ParcelFileDescriptor.class
Uri.class, AssetFileDescriptor.class
這裡其實和之前的邏輯類似,也是從glide中查表,繼續看關於這三對的註冊情況。仍然是在Glide構造方法中註冊的。- Uri.class, InputStream.class
.append(Uri.class, InputStream.class, new DataUrlLoader.StreamFactory<Uri>())
.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
.append(Uri.class, InputStream.class, new AssetUriLoader.StreamFactory(context.getAssets()))
.append(Uri.class, InputStream.class, new MediaStoreImageThumbLoader.Factory(context))
.append(Uri.class, InputStream.class, new MediaStoreVideoThumbLoader.Factory(context))
.append(Uri.class, InputStream.class, new UriLoader.StreamFactory(contentResolver))
.append(Uri.class, InputStream.class, new UrlUriLoader.StreamFactory())
- Uri.class, ParcelFileDescriptor.class
.append(Uri.class, ParcelFileDescriptor.class, new AssetUriLoader.FileDescriptorFactory(context.getAssets()))
.append(Uri.class, ParcelFileDescriptor.class, new UriLoader.FileDescriptorFactory(contentResolver))
- Uri.class, AssetFileDescriptor.class
.append(Uri.class,AssetFileDescriptor.class,
new UriLoader.AssetFileDescriptorFactory(contentResolver))
因此,上面三個StringLoader中,成員變數uriLoader分別為:
Uri.class, InputStream.class
MultiModelLoader:
[DataUrlLoader、HttpUriLoader、AssetUriLoader、MediaStoreImageThumbLoader、MediaStoreVideoThumbLoader、UriLoader、UrlUriLoader]Uri.class, ParcelFileDescriptor.class -> MultiModelLoader
MultiModelLoader:
[AssetUriLoader、UriLoader]- Uri.class, AssetFileDescriptor.class
UriLoader
ModelLoader最重要的功能就是通過buildLoadData建立相應的LoadData物件。
MultiModelLoader實現如下:
public LoadData<Data> buildLoadData(@NonNull Model model, int width, int height,
@NonNull Options options) {
Key sourceKey = null;
int size = modelLoaders.size();
List<DataFetcher<Data>> fetchers = new ArrayList<>(size);
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0; i < size; i++) {
ModelLoader<Model, Data> modelLoader = modelLoaders.get(i);
if (modelLoader.handles(model)) {
LoadData<Data> loadData = modelLoader.buildLoadData(model, width, height, options);
if (loadData != null) {
sourceKey = loadData.sourceKey;
fetchers.add(loadData.fetcher);
}
}
}
return !fetchers.isEmpty() && sourceKey != null
? new LoadData<>(sourceKey, new MultiFetcher<>(fetchers, exceptionListPool)) : null;
}
對於我們的三種情況來講,考慮到handles的過濾,model是”https://p.upyun.com/docs/cloud/demo.jpg”,因此最終建立的LoadData的fetchers資訊如下:
Uri.class, InputStream.class
LoadData: [{GlideUrl, HttpUrlFetcher},{GlideUrl, HttpUrlFetcher}]
Uri.class, ParcelFileDescriptor.class -> MultiModelLoader
LoadData: []
Uri.class, AssetFileDescriptor.class
LoadData:[{ObjectKey, AssetFileDescriptorLocalUriFetcher}]
至此返回的三個StringLoader以及最終建立的LoadData也就理清楚了。下面回到DecodeHelper的getLoadData中:
List<LoadData<?>> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
//noinspection ForLoopReplaceableByForEach to improve perf
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;
}
這裡就是呼叫前面建立的三個StringLoader,而後呼叫其buildLoadData方法,獲取到相應的LoadData物件,這裡我們可以知道,最終的loadData這個列表如下:
[
{LoadData: [{GlideUrl, HttpUrlFetcher},{GlideUrl, HttpUrlFetcher}]},
{LoadData:[{ObjectKey, AssetFileDescriptorLocalUriFetcher}]}]
至此,再看getCacheKeys的實現也就非常簡單了。
List<Key> getCacheKeys() {
if (!isCacheKeysSet) {
isCacheKeysSet = true;
cacheKeys.clear();
List<LoadData<?>> loadData = getLoadData();
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = loadData.size(); i < size; i++) {
LoadData<?> data = loadData.get(i);
if (!cacheKeys.contains(data.sourceKey)) {
cacheKeys.add(data.sourceKey);
}
for (int j = 0; j < data.alternateKeys.size(); j++) {
if (!cacheKeys.contains(data.alternateKeys.get(j))) {
cacheKeys.add(data.alternateKeys.get(j));
}
}
}
}
return cacheKeys;
}
因此此時的cacheKeys中的內容就是:
[GlideUrl,ObjectKey]
這裡我們分析了DecodeHelper類中,最複雜的獲取loadData的方法,沒有在註冊這塊花太大的精力,後面的分析中,我們會看到這個輔助類的使用。