美文网首页
Glide最新源码解析(三)- 加载Model

Glide最新源码解析(三)- 加载Model

作者: 烧伤的火柴 | 来源:发表于2019-10-08 11:25 被阅读0次

介绍

上一篇介绍了加载资源的流程,主要讲解了加载资源的主流程,本篇主要讲解从Model到Data是如何加载的?

ModelLoad

整个过程都是在DataFetcherGenerator的startNext()函数中执行的,我们分析主要的SourceGenerator

 @Override
  public boolean startNext() {
    if (dataToCache != null) {
      Object data = dataToCache;
      dataToCache = null;
      cacheData(data);
    }

    if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
      return true;
    }
    sourceCacheGenerator = null;

    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      loadData = helper.getLoadData().get(loadDataListIndex++);
      if (loadData != null
          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
              || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
        started = true;
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
  }

private boolean hasNextModelLoader() {
    return loadDataListIndex < helper.getLoadData().size();
  }

DecodeHelper#getLoadData()

  List<LoadData<?>> getLoadData() {
    if (!isLoadDataSet) {
      isLoadDataSet = true;
      loadData.clear();
      List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model); //1
      //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);//2 构建LoadData
        if (current != null) {
          loadData.add(current);
        }
      }
    }
    return loadData;
  }

第一步通过根据model(图片的网络地址)从Registry找到所有合适的ModelLoader

  @NonNull
  public <Model> List<ModelLoader<Model, ?>> getModelLoaders(@NonNull Model model) {
    List<ModelLoader<Model, ?>> result = modelLoaderRegistry.getModelLoaders(model);
    ...
    return result;
  }

Registry 和其他的子模块的注册器 是一个外观模式,将所有的子模块都封装到这里边,委托modelLoaderRegistry查找

  @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
  @NonNull
  public <A> List<ModelLoader<A, ?>> getModelLoaders(@NonNull A model) {
    List<ModelLoader<A, ?>> modelLoaders = getModelLoadersForClass(getClass(model));//根据Model查询所有tModelLoaders
    int size = modelLoaders.size();
    boolean isEmpty = true;
    List<ModelLoader<A, ?>> filteredLoaders = Collections.emptyList();
    //noinspection ForLoopReplaceableByForEach to improve perf
    for (int i = 0; i < size; i++) {
      ModelLoader<A, ?> loader = modelLoaders.get(i);
      if (loader.handles(model)) {
        if (isEmpty) {
          filteredLoaders = new ArrayList<>(size - i);
          isEmpty = false;
        }
        filteredLoaders.add(loader);
      }
    }
    return filteredLoaders;
  }

 @NonNull
  private synchronized <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

 @NonNull
  synchronized <Model> List<ModelLoader<Model, ?>> build(@NonNull Class<Model> modelClass) {
    try {
      List<ModelLoader<Model, ?>> loaders = new ArrayList<>();
      for (Entry<?, ?> entry : entries) {
        if (alreadyUsedEntries.contains(entry)) {
          continue;
        }
        if (entry.handles(modelClass)) {//1
          alreadyUsedEntries.add(entry);
          loaders.add(this.<Model, Object>build(entry));//build
          alreadyUsedEntries.remove(entry);
        }
      }
      return loaders;
    } catch (Throwable t) {
      alreadyUsedEntries.clear();
      throw t;
    }
  }

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

注释1处会过滤注册表中所有能够处理当前modelClass(String.class)的entry,然后调用factory的build方法构建一个ModelLoader对象。注册表的内容在Glide的构造方法中实现的。

Glide(...){
registry
        .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())+
...
}

过滤处符合条件的就是new DataUrlLoader.StreamFactory<String>(), new StringLoader.StreamFactory(), new StringLoader.FileDescriptorFactory() new StringLoader.AssetFileDescriptorFactory() 四个对象我们看一下具体的内容

public class StringLoader<Data> implements ModelLoader<String, Data> {
/** Factory for loading {@link InputStream}s from Strings. */
  public static class StreamFactory implements ModelLoaderFactory<String, InputStream> {

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

    @Override
    public void teardown() {
      // Do nothing.
    }
  }

  /** Factory for loading {@link ParcelFileDescriptor}s from Strings. */
  public static class FileDescriptorFactory
      implements ModelLoaderFactory<String, ParcelFileDescriptor> {

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

    @Override
    public void teardown() {
      // Do nothing.
    }
  }

  /** Loads {@link AssetFileDescriptor}s from Strings. */
  public static final class AssetFileDescriptorFactory
      implements ModelLoaderFactory<String, AssetFileDescriptor> {

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

    @Override
    public void teardown() {
      // Do nothing.
    }
  }
}
public final class DataUrlLoader<Model, Data> implements ModelLoader<Model, Data> {
public static final class StreamFactory<Model> implements ModelLoaderFactory<Model, InputStream> {

    private final DataDecoder<InputStream> opener;

    public StreamFactory() {
      opener =
          new DataDecoder<InputStream>() {
           ...
          };
    }

    @NonNull
    @Override
    public ModelLoader<Model, InputStream> build(@NonNull MultiModelLoaderFactory multiFactory) {
      return new DataUrlLoader<>(opener);
    }

    @Override
    public void teardown() {
      // Do nothing.
    }
  }
}

四个factory的build方法的到的是三个StringLoader对象和一个DataUrlLoader对象。回到getModelLoaders方法内filteredLoaders集合根据loader.handles(model)将不符合条件的剔除掉,其中DataUrlLoader接受处理的string,是"data:image"开始的,我们传入的是网络图片,所以整个不符合条件,剩下三个StringLoader对象。

DecodeHelper#getLoadData()方法的注释2处,根据model构建LoadData对象。

public class StringLoader<Data> implements ModelLoader<String, Data> {
  private final ModelLoader<Uri, Data> uriLoader;

  // Public API.
  @SuppressWarnings("WeakerAccess")
  public StringLoader(ModelLoader<Uri, Data> uriLoader) {
    this.uriLoader = uriLoader;
  }

  @Override
  public LoadData<Data> buildLoadData(
      @NonNull String model, int width, int height, @NonNull Options options) {
    Uri uri = parseUri(model);
    if (uri == null || !uriLoader.handles(uri)) {//1
      return null;
    }
    return uriLoader.buildLoadData(uri, width, height, options);
  }

  @Nullable
  private static Uri parseUri(String model) {
    Uri uri;
    if (TextUtils.isEmpty(model)) {
      return null;
      // See https://pmd.github.io/pmd-6.0.0/pmd_rules_java_performance.html#simplifystartswith
    } else if (model.charAt(0) == '/') {
      uri = toFileUri(model);
    } else {
      uri = Uri.parse(model);
      String scheme = uri.getScheme();
      if (scheme == null) {
        uri = toFileUri(model);
      }
    }
    return uri;
  }
...
}

StringLoader中的buildLoadData方法又委托uriLoader处理,uriLoader属性是在构造方法内赋值的,是在fatcory 的build的方法中传入的。三个工厂的build方法分别是:multiFactory.build(Uri.class, InputStream.class);multiFactory.build(Uri.class, ParcelFileDescriptor.class); multiFactory.build(Uri.class, AssetFileDescriptor.class),我们根据Uri.class 和 Data.class从Glide的注册表中找到合适的ModelLoader.Glide 的构造方法中有如下:

registry
        .append(Uri.class, InputStream.class, new DataUrlLoader.StreamFactory<Uri>())//1 data:image
        .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())//2
        .append(Uri.class, InputStream.class, new AssetUriLoader.StreamFactory(context.getAssets()))//3 android_asset
        .append(
            Uri.class,
            ParcelFileDescriptor.class,
            new AssetUriLoader.FileDescriptorFactory(context.getAssets()))//8
        .append(Uri.class, InputStream.class, new MediaStoreImageThumbLoader.Factory(context))//4
        .append(Uri.class, InputStream.class, new MediaStoreVideoThumbLoader.Factory(context))//5
        .append(Uri.class, InputStream.class, new UriLoader.StreamFactory(contentResolver))//6 file content
        .append(
            Uri.class,
            ParcelFileDescriptor.class,
            new UriLoader.FileDescriptorFactory(contentResolver))//9
        .append(
            Uri.class,
            AssetFileDescriptor.class,
            new UriLoader.AssetFileDescriptorFactory(contentResolver))//10
        .append(Uri.class, InputStream.class, new UrlUriLoader.StreamFactory())//7

由于篇幅有限,StringLoad中ModelLoaderFactory的multiFactory.build(Uri.class, XXX.class)的流程就不贴出来了,我就在这里直接说结果:
第一个StringLoader根据multiFactory.build(Uri.class, InputStream.class);方法的参数找到的是上边注释1-7的工厂build出来的loader集合构成的MultiModelLoader对象,如下所示,其中modelLoaders集合就是上边注释1-7的loader

  static class Factory {
    @NonNull
    public <Model, Data> MultiModelLoader<Model, Data> build(
        @NonNull List<ModelLoader<Model, Data>> modelLoaders,
        @NonNull Pool<List<Throwable>> throwableListPool) {
      return new MultiModelLoader<>(modelLoaders, throwableListPool);
    }
  }

第二个是StringLoader 根据Uri.class, ParcelFileDescriptor.class 从注册表中找到的是注释8-9的Factory build的AssetUriLoader和UriLoader 集合构建的MultiModelLoader 对象,伪代码如下:

  List<ModelLoader<Model, Data>> modelLoaders = new ArrayList<>(){
    {
    add(new AssetUriLoader());
    add(new UriLoader ());
    }
  };
  MultiModelLoader multiModelLoader = new MultiModelLoader(modelLoaders, throwableListPool);
  StringLoader stringLoader2 = new StringLoader(multiModelLoader ); 

这里可以看出MultiModelLoader和其他的ModelLoader 组成了 组合设计模式。
第三个StringLoader 对象根据Uri.class, AssetFileDescriptor.class 从注册表中找到的是注释10 处的AssetFileDescriptorFactory build的UriLoader 对象。伪代码如下:

StringLoader stringLoader3= new StringLoader(new UriLoader ()); 

回到StringLoader#buildLoadData方法的注释1处,根据传入的String解析是协议是http类型的,上边三个StringLoader中的10个modelLoader只有UrlUriLoader和HttpUriLoader两个类可以处理http类型的。这两个类也是通过传入的其他ModelLoader处理的加载任务,通过他们内部类的工厂的build方法传入的loader,两个类都是传入的multiFactory.build(GlideUrl.class, InputStream.class),根据GlideUrl.class, InputStream.class从注册表中找到的是HttpGlideUrlLoader类。MultiModelLoader#buildLoadData

  @Override
  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);//1
        }
      }
    }
    return !fetchers.isEmpty() && sourceKey != null
        ? new LoadData<>(sourceKey, new MultiFetcher<>(fetchers, exceptionListPool))
        : null;
  }

上边注释1处的fetchers添加的是两个HttpGlideUrlLoader中buildLoadData构建出来的HttpUrlFetcher对象
回到开始的DecodeHelper#getLoadData()方法的伪代码是:

  List<LoadData<?>> getLoadData() {
    List<DataFetcher<Data>> fetchers = new ArrayList<>(2);
    fetchers.add(new HttpUrlFetcher(new GlideUrl("图片的地址")));
    fetchers.add(new HttpUrlFetcher(new GlideUrl("图片的地址")));
    MultiFetcher multiFetcher = new MultiFetcher(fetchers, throwableListPool);
    LoadData<?> current = new LoadData("图片的地址",multiFetcher); 
    loadData.add(current);
    return loadData;
  }

现在再看一下SourceGenerator#startNext方法

 @Override
  public boolean startNext() {
    if (dataToCache != null) {
      Object data = dataToCache;
      dataToCache = null;
      cacheData(data);
    }

    if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
      return true;
    }
    sourceCacheGenerator = null;

    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      loadData = helper.getLoadData().get(loadDataListIndex++);
      if (loadData != null
          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
              || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {//getDataClass()=InputStream.class
        started = true;
        loadData.fetcher.loadData(helper.getPriority(), this);//2
      }
    }
    return started;
  }

helper.hasLoadPath(loadData.fetcher.getDataClass())这个过程会根据 Data ,Resource和 TrancodeResource检查注册表是否有合适的LoadPath,后边分析解码和转码的时候也会用到,找个分析就放到后边的文章。接着会执行注释2的地方,将结果回调给当前的类

  @Override
  public void onDataReady(Object data) {
    //
    DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
    if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
      dataToCache = data;
      // We might be being called back on someone else's thread. Before doing anything, we should
      // reschedule to get back onto Glide's thread.
      cb.reschedule();
    } else {
      cb.onDataFetcherReady(
          loadData.sourceKey,
          data,
          loadData.fetcher,
          loadData.fetcher.getDataSource(),
          originalKey);
    }
  }

当设置使用缓存的时候,会将从model fetch到的data缓存到磁盘中,这里的缓存的数据就是原始数据,在下次加载资源的时候会用到,上一篇文章已经分析过了。所以这里分析不使用缓存的时候,将data回传到DecodeJob的onDataFetcherReady中。
到这里从Model加载成Data的过程已经全部分析完了。

那么多从Model加载到Data 的DataFetcher中,我们选择一个分析一下,我们加载url的string的时候,使用的是HttpUrlFetcher中的loadData方法。所以分一下HttpUrlFetcher#loadData

HttpUrlFetcher#loadData

 @Override
  public void loadData(
      @NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
    long startTime = LogTime.getLogTime();
    try {
      InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
      callback.onDataReady(result);
    } catch (IOException e) {
      callback.onLoadFailed(e);
    } 
    ...
  }

  private InputStream loadDataWithRedirects(
      URL url, int redirects, URL lastUrl, Map<String, String> headers) throws IOException {
    if (redirects >= MAXIMUM_REDIRECTS) {//最大重定向次数是5次
      throw new HttpException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
    } else {
      try {
        if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {//防止死循环
          throw new HttpException("In re-direct loop");
        }
      } catch (URISyntaxException e) {
        // Do nothing, this is best effort.
      }
    }

    urlConnection = connectionFactory.build(url);//HttpURLConnection
    for (Map.Entry<String, String> headerEntry : headers.entrySet()) {//添加header
      urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
    }
    urlConnection.setConnectTimeout(timeout);//连接超时 2500
    urlConnection.setReadTimeout(timeout);
    urlConnection.setUseCaches(false);
    urlConnection.setDoInput(true);
    urlConnection.setInstanceFollowRedirects(false);
    urlConnection.connect();
    stream = urlConnection.getInputStream();
    if (isCancelled) {
      return null;
    }
    final int statusCode = urlConnection.getResponseCode();
    if (isHttpOk(statusCode)) {//1
      return getStreamForSuccessfulRequest(urlConnection);//3
    } else if (isHttpRedirect(statusCode)) {//2
      String redirectUrlString = urlConnection.getHeaderField("Location");
      ...
      URL redirectUrl = new URL(url, redirectUrlString);
      ...
      cleanup();
      return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
    } else if (statusCode == INVALID_STATUS_CODE) {
      throw new HttpException(statusCode);
    }
    ...
  }

loadData方法调用了 重定向加载方法loadDataWithRedirects,上边注释1处是加载成功返回结果,注释2处是响应码是3XX 重定向,从响应头中拿到重定向的地址进行下一次的请求。我们看一下注释3处

 private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)
      throws IOException {
    if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
      int contentLength = urlConnection.getContentLength();
      stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);
    } else {
      stream = urlConnection.getInputStream();
    }
    return stream;
  }

没有ContentEncoding的时候,使用ContentLengthInputStream,有ContentEncoding的时候 使用InputStream。
至此整个流程已经分析完毕。

相关文章

网友评论

      本文标题:Glide最新源码解析(三)- 加载Model

      本文链接:https://www.haomeiwen.com/subject/xqfructx.html