Glide 4.x之ModelLoader简单分析

, 其实分析glide的源码,不仅可以让人能更好的了解其内部工作原理以便能更好的使用该框架,更重要的是能从阅读代码种学习一些其他的东西,比如代码的组织设计策略,设计模式的灵活应用,甚至你可以剥离出一些有用的设计理念来应用到自己的开发中去,真是可以让你获益良多。

在《Glide之请求网络图片数据流程解析》这面博文中我就就接触了ModelLoader这个glide的组件,但是没有细说,本文就简单分析下。
具体的体现着这一句:

public boolean startNext() {
      //省略部分代码
      loadData = helper.getLoadData().get(loadDataListIndex++);

     //省略部分代码
  }

先进入DecodeHelper的getLoadData方法来看看ModelLoader的应用:

  //返回一个LoadData对象的集合
  List<LoadData<?>> getLoadData() {
      //省略部分代码
      //1、获取ModelLoader对象的集合
      List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);

      int size = modelLoaders.size();
      //2、遍历modelLoaders集合
      for (int i = 0; i < size; i++) {
        ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
        //3、通过modelLoader对象获取LoadData
        LoadData<?> current =
            modelLoader.buildLoadData(model, width, height, options);
        if (current != null) {
          loadData.add(current);
        }
      }

    return loadData;
  }

getLoadData方法主要做了两件事:
1、通过Registry对象的getModelLoaders(model)获取ModelLoaders对象的集合。注意如果这里的model对象就是通过load方法传入的对象,比如你load(“http://xxxx.img“),那么这个model就是String类型的对象。此篇博文中我们就假设model为String的类型来分析。(getModelLoaders方法后面会具体分析)
2、遍历集合,使得每一个ModelLoader通过buildLoadData创建一个LoadData对象。

那么这里可以引申出两个问题:
1、ModelLoader是什么?
2、Registry对象是什么时候初始化的?
3、Registry对象里持有的ModelLoader集合所包含的各个ModelLoadder 是什么时候添加的?
下面就带着上面的问题来继续下面的博文。ModelLoader是一个接口,该接口里有一个名为LoadData的内部类,且该接口提供了一个buildLoadData方法来创建一个LoadData对象,该方法可以看作是一个工厂方法:

public interface ModelLoader<Model, Data> {

  //加载数据用的LoadData
  class LoadData<Data> {
    public final Key sourceKey;
    public final List<Key> alternateKeys;
    //真正加载数据的地方
    public final DataFetcher<Data> fetcher;

    public LoadData(Key sourceKey, DataFetcher<Data> fetcher) {
      this(sourceKey, Collections.<Key>emptyList(), fetcher);
    }

    public LoadData(Key sourceKey, List<Key> alternateKeys, DataFetcher<Data> fetcher) {

    }
  }


  //创建LoadData的工厂方法
  LoadData<Data> buildLoadData(Model model, int width, int height, Options options);


  boolean handles(Model model);
}

纵观LoadData的代码,里面除了两个构造器用来初始化LoadData的属性之外,并没有提供任何的方法来加载数据,且里面DataFetcher的类引用,我们有理由相信真正加载数据的类就是DataFetcher!!!当然在《Glide之请求网络图片数据流程解析》已经略作说明。

在glide中ModelLoader有如下实现:
这里写图片描述

另外glide中还提供了ModelLoaderFactory的工厂接口来创建ModelLoader:

public interface ModelLoaderFactory<T, Y> {

  ModelLoader<T, Y> build(MultiModelLoaderFactory multiFactory);
  void teardown();
}

该接口工厂glide中也提供了一些实现:
这里写图片描述
需要注意的这这些工厂类的具体实现是都是ModelLoader接口具体实现的内部类。
ModelLoader 先简单分析到这儿,从代码设计上来看其主要作用就是创建一个LoadData对象来处理具体到业务逻辑。上文中我们是通过Registry对象来获取ModelLoader的集合的,Registry的初始化是在Glide的构造器里面初始化的,且初始化Registry的同时也把glide内部提供的ModelLoaderFactory的具体实现(见上图)注册到Registry类中:

 Glide(//省略一大堆参数) {
    //省略部分代码

    registry = new Registry();
        /*省略部分代码*/
        /*注册ModelLoader和ModelLoaderFactory相关的对象*/
        registry.append(File.class, ByteBuffer.class, new ByteBufferFileLoader.Factory())
        .append(File.class, InputStream.class, new FileLoader.StreamFactory())
        .append(File.class, File.class, new FileDecoder())
        .append(File.class, ParcelFileDescriptor.class, new FileLoader.FileDescriptorFactory())

        .append(File.class, File.class, new UnitModelLoader.Factory<File>())
        .register(new InputStreamRewinder.Factory(arrayPool))
        .append(int.class, InputStream.class, new ResourceLoader.StreamFactory(resources))
        .append(
                int.class,
                ParcelFileDescriptor.class,
                new ResourceLoader.FileDescriptorFactory(resources))
        .append(Integer.class, InputStream.class, new ResourceLoader.StreamFactory(resources))
        .append(
                Integer.class,
                ParcelFileDescriptor.class,
                new ResourceLoader.FileDescriptorFactory(resources))
        .append(String.class, InputStream.class, new DataUrlLoader.StreamFactory())
        .append(String.class, InputStream.class, new StringLoader.StreamFactory())
        .append(String.class, ParcelFileDescriptor.class, new StringLoader.FileDescriptorFactory())
        .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(context.getContentResolver()))
        .append(Uri.class, ParcelFileDescriptor.class,
             new UriLoader.FileDescriptorFactory(context.getContentResolver()))
        .append(Uri.class, InputStream.class, new UrlUriLoader.StreamFactory())
        .append(URL.class, InputStream.class, new UrlLoader.StreamFactory())
        .append(Uri.class, File.class, new MediaStoreFileLoader.Factory(context))
        .append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
        .append(byte[].class, ByteBuffer.class, new ByteArrayLoader.ByteBufferFactory())
        .append(byte[].class, InputStream.class, new ByteArrayLoader.StreamFactory())
        /*省略部分代码*/


  }

上面除了registry方法之外,大部分都是通过prepend和append方法来注册ModelLoader或者ModelLoaderFactory对象的,进入其append方法来看看:

 private final ModelLoaderRegistry modelLoaderRegistry;
 public Registry() {
    this.modelLoaderRegistry = new ModelLoaderRegistry(exceptionListPool);
     //省略部分代码
  }

 public <Model, Data> Registry append(Class<Model> modelClass, Class<Data> dataClass,
      ModelLoaderFactory<Model, Data> factory) {
    modelLoaderRegistry.append(modelClass, dataClass, factory);
    return this;
  }

最终将添加的ModelLoaderFactory都通过modelLoaderRegistry对象的append来保存起来。所以进入ModelLoaderRegistry类的append方法去看:

  //一个MultiModelLoaderFactory对象的引用
  private final MultiModelLoaderFactory multiModelLoaderFactory;

 //构造器
  ModelLoaderRegistry(MultiModelLoaderFactory multiModelLoaderFactory) {
    this.multiModelLoaderFactory = multiModelLoaderFactory;
  }

  public synchronized <Model, Data> void append(Class<Model> modelClass, Class<Data> dataClass,
      ModelLoaderFactory<Model, Data> factory) {
    //MultiModelLoaderFactory的append  
    multiModelLoaderFactory.append(modelClass, dataClass, factory);
    cache.clear();
  }

几经辗转,最总通过Registry注册进来的ModerLoaderFactory是转移到了MultiModelLoaderFactory这个类中,看看该类的方法都是做了什么:

synchronized <Model, Data> void append(Class<Model> modelClass, Class<Data> dataClass,
      ModelLoaderFactory<Model, Data> factory) {
    //调用add方法  
    add(modelClass, dataClass, factory, true);
  }
//entry集合类
private final List<Entry<?, ?>> entries = new ArrayList<>();

  private <Model, Data> void add(Class<Model> modelClass, Class<Data> dataClass,
      ModelLoaderFactory<Model, Data> factory, boolean append) {
    //用add方法的参数构建一个entry对象  
    Entry<Model, Data> entry = new Entry<>(modelClass, dataClass, factory);
    entries.add(append ? entries.size() : 0, entry);
  }

上面的方法也不复杂,无非就是将Registry添加进来的factory最终构成Enry对象添加到entries集合里面,说白了就是几个对象的辗转腾挪,然后构建一个Entry来统一管理,放入到集合里面而已。

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

写到此处,先捋一捋上面的讲述:
1、ModelLoader主要负责创建一个LoadData对象
2、ModelLoaderFactory顾名思义就是构建ModelLoader对象
3、通过Registry这个对象将各个ModelLoaderFactory 注册进来,说白来就是注册到MultiModelLoaderFactory中,只不过这些ModelLoaderFactory最终又单独的剥离出一个Entry对象放入集合中,从某角度来看Registry倒是可以看作一个门面模式来看待。

综合以上三点想要获取LoadData对象,也只是简单的几步而已:
1、遍历MultiModelLoaderFactory的entries集合,根据model类型获取对应类型的ModelLoaderfactory对象
2、调用ModelLoaderFactory的build方法得到一个ModelLoader对象
3、调用ModelLoader的buildLoadData方法获取具体的LoadData对象。
上面说明有些啰嗦了,可以用下图表示其关系:
这里写图片描述
到此为止这几个类的关系算是介绍完毕,那么让我们继续文章开头所说的Registry类中的getModelLoaders(Model)方法,其中model我们仍然认为是String类型的数据,根据上图的关系我们直接看MultiModelLoaderRegistry类中的getModelLoaders方法:

 public synchronized <A> List<ModelLoader<A, ?>> getModelLoaders(A model) {
    //1、根据model获取ModelLoader集合
    List<ModelLoader<A, ?>> modelLoaders = getModelLoadersForClass(getClass(model));
    //2、遍历集合中的ModelLoader对象,判断ModelLoader是否处理了当前的model
    int size = modelLoaders.size();
    List<ModelLoader<A, ?>> filteredLoaders = new ArrayList<>(size);
    for (int i = 0; i < size; i++) {
      ModelLoader<A, ?> loader = modelLoaders.get(i);
      //如果该ModelLoader的handles返回了true
      if (loader.handles(model)) {
        filteredLoaders.add(loader);
      }
    }
    return filteredLoaders;
  }

上面的核心代码就是调用getModelLoadersForClass方法来获取ModelLoader集合,但是呢,glide中有这么多的ModelLoader到底哪些是当前所需要的呢,这就要根据ModelLoader的handles来匹配了,这个不必细说,我们来看看getModelLoadersForClass都做了哪些:

 //此时modelClass是String.class
 private <A> List<ModelLoader<A, ?>> getModelLoadersForClass(Class<A> modelClass) {
    //根据modelClass来获取缓存中的ModelLoader集合
    List<ModelLoader<A, ?>> loaders = cache.get(modelClass);
    //集合不存在则调用multiModelLoaderFactory的build方法
    if (loaders == null) {
      loaders = Collections.unmodifiableList(multiModelLoaderFactory.build(modelClass));
      cache.put(modelClass, loaders);
    }
    return loaders;
  }

getModelLoadersForClass方法逻辑很简单,就是先从缓存中通过modelClass获取对应的ModelLoader集合,缓存没有则调用multiModelLoaderFactory的build方法,该方法如下:

 synchronized <Model> List<ModelLoader<Model, ?>> build(Class<Model> modelClass) {

      List<ModelLoader<Model, ?>> loaders = new ArrayList<>();
      //遍历entries集合
      for (Entry<?, ?> entry : entries) {
        //省略部分代码
        if (entry.handles(modelClass)) {
           //省略部分代码
           //根据entry对象创建具体的ModelLoader
          loaders.add(this.<Model, Object>build(entry));
          //省略部分代码
        }
      }
      return loaders;

  }

上面代码的主要逻辑就是遍历通过Registry注册而声称的entries集合,然后根据modelClass来匹配具体entry,然后调用build(entry)方法来获取具体的ModelLoader对象,根据上面的讲解以及上面的图示,我们不难猜出build(entry)方法其实就是调用了其内部的ModelLoaderFactory对象build而已,有代码为证:

  private <Model, Data> ModelLoader<Model, Data> build(Entry<?, ?> entry) {
    return (ModelLoader<Model, Data>) 
//调用factory的build方法。   Preconditions.checkNotNull(entry.factory.build(this));
  }

到此为止ModelLoader对象的真正创建过程已经解析完毕,其实对象的创建过程无非就是那么几种,但是我们的glide确百转千回,为了得到LoadData对象先是提供了ModelLoader接口,又提供了ModelLoaderFactory;总的来说由ModelLoaderFactory接口创建ModelLoader对象,再由ModelLoader来创建LoadData可以说做了大量的“额外”工作,但是呢不得不说这个“额外”工作从代码设计的觉度来说着实可以用来借鉴一二,具体怎么借鉴,仁者见仁智者见智了,有时候某些东西还真是只可意会不可言谈。

在《Glide之请求网络图片数据流程解析》中我们知道最终是通过HttpUrlFetcher来完成最终的数据加载的(当然缓存不算),我们本篇就来详细说说这个HttpUrlFetcher是怎么蹦跶出来的。

从上文我们知道了获取ModelLoader的方法,且因为我们load方法传入的是一个String类型url,也就是说我们的model就是String.class,在Registry中注册ModelLoderFactory的时候,以String.class为model的ModelLoaderFactory有如下几个:

         new ResourceLoader.FileDescriptorFactory(resources))
        .append(String.class, InputStream.class, new DataUrlLoader.StreamFactory())
        .append(String.class, InputStream.class, new StringLoader.StreamFactory())
        .append(String.class, ParcelFileDescriptor.class, new StringLoader.FileDescriptorFactory())

还是进入getLoadData的debug模式,我们获取的modelLoader 的集合其实是一个StringLoader:
这里写图片描述
因为我们的model传入的是http的URL,所以看上面的StringLoader即可。先来看看ModelLoaderFactory初始化StringLoader的build方法:

  @Override
    public ModelLoader<String, InputStream> build(MultiModelLoaderFactory multiFactory) {
      return new StringLoader<>(multiFactory.build(Uri.class, InputStream.class));
    }

可以看出StringLoader的初始化是需要另外一个ModdelLoader 作为参数的,从StringLoader的构造器代码中可以出这一点:

  private final ModelLoader<Uri, Data> uriLoader;

  public StringLoader(ModelLoader<Uri, Data> uriLoader) {
    this.uriLoader = uriLoader;
  }

uriLoader的初始化是通过如下代码来初始化

//注意此处是两个参数的build方法,上文讲的是一个参数的build方法
multiFactory.build(Uri.class, InputStream.class)

此时通过multiFactory构建ModelLoader的model类型是Uri.class,当然以Uri.class为Model的Mode Loader也很多,在此不一一列出,看看此build重载方法都做了什么:

//modelClass == Uri.class
//dataClass == InputStream.class
 public synchronized <Model, Data> ModelLoader<Model, Data> build(Class<Model> modelClass,
      Class<Data> dataClass) {
    try {
      List<ModelLoader<Model, Data>> loaders = new ArrayList<>();
      for (Entry<?, ?> entry : entries) {
        //注意此处
        if (entry.handles(modelClass, dataClass)) {
          alreadyUsedEntries.add(entry);
          loaders.add(this.<Model, Data>build(entry));
          alreadyUsedEntries.remove(entry);
        }
      }

      //当model匹配不止一个ModelLoader的时候
      if (loaders.size() > 1) {
        //此factory为MultiModelLoaderFactory的嵌套类
        return factory.build(loaders, exceptionListPool);
      } else if (loaders.size() == 1) {
        return loaders.get(0);
      } else {
        //省略部分代码
      }
    } catch (Throwable t) {
      //省略部分代码
    }
  }

阅读上面代码发现,当model.class匹配了多个ModelLoader的时候调用了factory的build方法返回一个MultiModelLoader,该build 的方法的第一个参数就是我们匹配的多个ModelLoader对象的集合:

  static class Factory {
    public <Model, Data> MultiModelLoader<Model, Data> build(
        List<ModelLoader<Model, Data>> modelLoaders, Pool<List<Exception>> exceptionListPool) {
        //返回MultiModelLoader对象
      return new MultiModelLoader<>(modelLoaders, exceptionListPool);
    }
  }

所以我们构建StringLoader的时候需要的uriLoader的时候我们传了两个参数进行ModelLoader的匹配:Uri.class和InputStream.class,其中Uri.class 作为model.class而InputStream.class作为data.class来调用entryhandles(modelClass, dataClass)来进行匹配,在Registry注册ModelLoaderFactory的时候负责上述两种的MolderLaoderFactory如下:

.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())

.append(Uri.class, InputStream.class, new AssetUriLoader.StreamFactory(context.getAssets()))

  .append(Uri.class, InputStream.class, new UrlUriLoader.StreamFactory())

 //以下仍然省略几种

所以此时StringLoader的uriLoader就是一个MultiModelLoader对象,也就是说MultiModelLoader里面有若干个匹配model.class和dataclass的集合,且从面的debug图来看,集合中的一个ModerLoader就是HttpUriLoder.

按照上文缩手,ModelLaoder的初始化是由Factory来完成的,所以我们只关心HttpUriLoader的Factory,看看该Factory的build方法:


    @Override
    public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {
      return new 
      HttpUriLoader(multiFactory.build(GlideUrl.class, InputStream.class));
    }

同样的,在构造HttpUriLoader的时候需要根StringLoader一样同样需要一个ModelLoader对象,该对象的变量名是urlLoader,且传入的model为GlideUrl.class,data.class为InputStream.class,符合提条件的ModeLoder就是HttpGlideUrlLoader。

 .append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())

通过前面几片博文的讲解,我们知道ModelLoader提供buildLoadData方法来返回一个LoadData方法来加载数据,所以看看HttpUriLoader的buildData返回的LoadData是什么鬼:

  public LoadData<InputStream> buildLoadData(Uri model, int width, int height, Options options) {
    //调用HttpGlideUrlLoader的buildLoadData方法
    return urlLoader.buildLoadData(new GlideUrl(model.toString()), width, height, options);
  }

可以发现HttpUriLoader的buildLoadData方法直接调用了HttpGlideUrlLoader的buildLoadData方法,该方法如下:

 public LoadData<InputStream> buildLoadData(GlideUrl model, int width, int height,
     //省略部分代码
    return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
  }

看到了把,绕了十万八千里HttpUrlFetcher对象犹抱琵琶“不”遮面了,根据《Glide之请求网络图片数据流程解析》这片博文,HttpUrlFetcher就是访问服务器获取图片说句的类了。

所以LoadData只是一个外壳,真正加载数据的还是LoadData持有的DataFetcher接口,该接口在glide的实现类由如下几个:
这里写图片描述
当然到了此处,本篇博文就结束类,回头再看看着实有些啰嗦,不当之处,欢迎批评指正

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

郭梧悠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值