您当前的位置: 首页 >  ide

郭梧悠

暂无认证

  • 1浏览

    0关注

    402博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Glide 4.x之ModelLoader简单分析

郭梧悠 发布时间:2017-11-05 12:34:39 ,浏览量:1

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

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

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

     //省略部分代码
  }

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

  //返回一个LoadData对象的集合
  List> modelLoaders = glideContext.getRegistry().getModelLoaders(model);

      int size = modelLoaders.size();
      //2、遍历modelLoaders集合
      for (int i = 0; i < size; i++) {
        ModelLoader modelLoader = modelLoaders.get(i);
        //3、通过modelLoader对象获取LoadData
        LoadData> entries = new ArrayList();

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

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

private static class Entry {
    private final Class modelClass;
    @Synthetic final Class dataClass;
    @Synthetic final ModelLoaderFactory 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  List getModelLoaders(A model) {
    //1、根据model获取ModelLoader集合
    List modelLoaders = getModelLoadersForClass(getClass(model));
    //2、遍历集合中的ModelLoader对象,判断ModelLoader是否处理了当前的model
    int size = modelLoaders.size();
    List filteredLoaders = new ArrayList(size);
    for (int i = 0; i < size; i++) {
      ModelLoader 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  List getModelLoadersForClass(Class modelClass) {
    //根据modelClass来获取缓存中的ModelLoader集合
    List 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  List build(Class modelClass) {

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

  }

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

  private  ModelLoader build(Entry entry) {
    return (ModelLoader) 
//调用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 build(MultiModelLoaderFactory multiFactory) {
      return new StringLoader(multiFactory.build(Uri.class, InputStream.class));
    }

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

  private final ModelLoader uriLoader;

  public StringLoader(ModelLoader 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  ModelLoader build(Class modelClass,
      Class dataClass) {
    try {
      List loaders = new ArrayList();
      for (Entry entry : entries) {
        //注意此处
        if (entry.handles(modelClass, dataClass)) {
          alreadyUsedEntries.add(entry);
          loaders.add(this.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  MultiModelLoader build(
        List modelLoaders, Pool 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 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 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 buildLoadData(GlideUrl model, int width, int height,
     //省略部分代码
    return new LoadData(url, new HttpUrlFetcher(url, timeout));
  }

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

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

关注
打赏
1663674776
查看更多评论
立即登录/注册

微信扫码登录

0.0413s