美文网首页
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