DecodeHelper類相關(guān)方法分析

DecodeHelper類中丐重,調(diào)用的方法腔召,涉及到的東西比較多,最主要的是包括管理組件注冊以擴展或替換Glide的默認加載扮惦,解碼和編碼邏輯的Registry類臀蛛。在Glide類的構(gòu)造方法中,如下:

 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))

         ....

在這里崖蜜,注冊了一系列的信息浊仆,我們這里著手關(guān)注String.class這塊的ModelLoader對象。一般Glide加載一個url字符串的時候豫领,就是通過String.class取對應(yīng)的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加入了四條數(shù)據(jù),Entry的結(jié)構(gòu)如下:

  private static class Entry<Model, Data> {
    private final Class<Model> modelClass;
    @Synthetic final Class<Data> dataClass;
    @Synthetic final ModelLoaderFactory<? extends Model, ? extends Data> factory;
    
  }

暫且先有點這個印象等恐,注冊的過程不是我們要重點關(guān)注的洲劣,Registry不過只是一種構(gòu)造ModelLoader的實現(xiàn)而已备蚓,下面我們先分析DecodeHelper的getLoadData的實現(xiàn)。

  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,它的實現(xiàn)如下:

  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;
  }

繼續(xù)跟進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;
  }

先是調(diào)用取model的class類型吞获,然后通過getModelLoadersForClass去獲取前面注冊進去的loaders信息况凉。獲取到之后,通過ModelLoader的handles方法進行一次過濾各拷。最終返回茎刚,我們繼續(xù)分析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方法返回值決定的。下面繼續(xù)分析這個方法粮坞。

 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我們上面已經(jīng)有所介紹蚊荣,和String有關(guān)的目前有四個,重點就是entry.handles方法的返回值莫杈,決定其是否能加入到loaders中互例,而在這個方法中,實現(xiàn)就是看是否和modelClass能夠匹配筝闹,所以此時和String.class能匹配的就是上面已經(jīng)列出的媳叨。

        .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())

接下來會調(diào)用build方法傳入entry獲取一個ModelLoader對象,build實現(xiàn)如下:

  private <Model, Data> ModelLoader<Model, Data> build(@NonNull Entry<?, ?> entry) {
    return (ModelLoader<Model, Data>) Preconditions.checkNotNull(entry.factory.build(this));
  }

這里就是調(diào)用entry對應(yīng)的factory對象去構(gòu)建ModelLoader关顷。這里四個工廠分別是DataUrlLoader.StreamFactory糊秆、StringLoader.StreamFactory、StringLoader.FileDescriptorFactory與StringLoader.AssetFileDescriptorFactory议双。分別構(gòu)造了DataUrlLoader痘番、StringLoader、StringLoader平痰、StringLoader四個loader對象汞舱。
對于一個url,這里我們的是:"https://p.upyun.com/docs/cloud/demo.jpg",ModelLoader中有一個handles方法宗雇,表明此類型是否可以被自己處理昂芜。
DataUrlLoader的實現(xiàn)如下:

 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的實現(xiàn)如下:

  public boolean handles(@NonNull String model) {
    return true;
  }

顯然此時泌神,是可以處理這個url對象的良漱。 三個Factory的build方法的實現(xiàn)如下:

 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));
    }

  }

這塊相對有點復(fù)雜,在StringLoader中有一個成員變量uriLoader腻扇,它也是由entry傳入的MultiModelLoaderFactory的build方法繼續(xù)創(chuàng)建的债热。這里我們可以看到有一個遞歸的過程,其實也很好理解幼苛,雖然三個都是StringLoader窒篱,但內(nèi)部的成員變量uriLoader不一樣。分別是三對:

  1. Uri.class, InputStream.class
  2. Uri.class, ParcelFileDescriptor.class
  3. Uri.class, AssetFileDescriptor.class
    這里其實和之前的邏輯類似舶沿,也是從glide中查表墙杯,繼續(xù)看關(guān)于這三對的注冊情況。仍然是在Glide構(gòu)造方法中注冊的括荡。
  • 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創(chuàng)建相應(yīng)的LoadData對象州邢。
MultiModelLoader實現(xiàn)如下:

 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"量淌,因此最終創(chuàng)建的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以及最終創(chuàng)建的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;
  }

這里就是調(diào)用前面創(chuàng)建的三個StringLoader嫌褪,而后調(diào)用其buildLoadData方法呀枢,獲取到相應(yīng)的LoadData對象,這里我們可以知道笼痛,最終的loadData這個列表如下:

[

{LoadData: [{GlideUrl, HttpUrlFetcher},{GlideUrl, HttpUrlFetcher}]},
{LoadData:[{ObjectKey, AssetFileDescriptorLocalUriFetcher}]}

]

至此硫狞,再看getCacheKeys的實現(xiàn)也就非常簡單了。

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中的內(nèi)容就是:

[GlideUrl,ObjectKey]

這里我們分析了DecodeHelper類中晃痴,最復(fù)雜的獲取loadData的方法残吩,沒有在注冊這塊花太大的精力,后面的分析中倘核,我們會看到這個輔助類的使用泣侮。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市紧唱,隨后出現(xiàn)的幾起案子活尊,更是在濱河造成了極大的恐慌隶校,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蛹锰,死亡現(xiàn)場離奇詭異深胳,居然都是意外死亡,警方通過查閱死者的電腦和手機铜犬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門舞终,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人癣猾,你說我怎么就攤上這事敛劝。” “怎么了纷宇?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵夸盟,是天一觀的道長。 經(jīng)常有香客問我像捶,道長上陕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任拓春,我火速辦了婚禮释簿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘痘儡。我一直安慰自己,他們只是感情好枢步,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布沉删。 她就那樣靜靜地躺著,像睡著了一般醉途。 火紅的嫁衣襯著肌膚如雪矾瑰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天隘擎,我揣著相機與錄音殴穴,去河邊找鬼。 笑死货葬,一個胖子當(dāng)著我的面吹牛采幌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播震桶,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼休傍,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蹲姐?” 一聲冷哼從身側(cè)響起磨取,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤人柿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后忙厌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凫岖,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年逢净,在試婚紗的時候發(fā)現(xiàn)自己被綠了哥放。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡汹胃,死狀恐怖婶芭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情着饥,我是刑警寧澤犀农,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站宰掉,受9級特大地震影響呵哨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜轨奄,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一孟害、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧挪拟,春花似錦挨务、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至惯雳,卻和暖如春朝巫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背石景。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工劈猿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人潮孽。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓揪荣,卻偏偏與公主長得像,于是被迫代替她去往敵國和親往史。 傳聞我的和親對象是個殘疾皇子变逃,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容