1. 程式人生 > >DecodeHelper類相關方法分析

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不一樣。分別是三對:

  1. Uri.class, InputStream.class
  2. Uri.class, ParcelFileDescriptor.class
  3. 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的方法,沒有在註冊這塊花太大的精力,後面的分析中,我們會看到這個輔助類的使用。