美文网首页Android开发
Glide源码分析之数据拉取

Glide源码分析之数据拉取

作者: juexingzhe | 来源:发表于2020-07-23 14:44 被阅读0次

    同样地,开始之前先思考1个问题:

    1. Glide是怎么实现那么多资源Model的加载的?比如可以从Url、Asset、FileDescriptor、Uri、File等来源加载数据

    1.ModelLoader

    Glide的所有数据加载都实现一个接口ModelLoader<Model, Data>,其中Model类型就是来源的类型,Data是加载得到的数据类型,看下接口的具体源码,包含一个内部类LoadData,和两个接口方法,一个buildLoadData方法用来构造返回一个LoadData,另外一个方法handles用来返回这个ModelLoader能否处理这个ModelLoadData这个类里面有三个字段,一个sourceKey用来表示这次下载,一个DataFetcher用来获取不在缓存中的数据:

    public interface ModelLoader<Model, Data> {
    
      /**
       * @param <Data> The type of data that well be loaded.
       */
      class LoadData<Data> {
        public final Key sourceKey;
        public final List<Key> alternateKeys;
        public final DataFetcher<Data> fetcher;
    
        public LoadData(@NonNull Key sourceKey, @NonNull DataFetcher<Data> fetcher) {
          this(sourceKey, Collections.<Key>emptyList(), fetcher);
        }
    
        public LoadData(@NonNull Key sourceKey, @NonNull List<Key> alternateKeys,
            @NonNull DataFetcher<Data> fetcher) {
          this.sourceKey = Preconditions.checkNotNull(sourceKey);
          this.alternateKeys = Preconditions.checkNotNull(alternateKeys);
          this.fetcher = Preconditions.checkNotNull(fetcher);
        }
      }
    
      @Nullable
      LoadData<Data> buildLoadData(@NonNull Model model, int width, int height,
          @NonNull Options options);
    
      boolean handles(@NonNull Model model);
    }
    
    

    看下Glide里面所有的实现类,这些种类就代表了Glide支持从哪些数据类型下载数据:

    ModelLoader.PNG

    比如我们看一个最常用的从http/https uris下载数据的UrlUriLoader, 先看它的接口方法handles,如果scheme类型是http/https类型的就返回true;另外一个接口方法buildLoadData通过字段urlloader返回一个LoadData,还有一个内部工厂类StramFactory, UrlUriLoader不建议外面通过构造函数实例化,而是通过这个工厂类的build方法进行实例化。基本上ModelLoader的实现类都是这种设计模式。

    // UrlUriLoader.java
    public class UrlUriLoader<Data> implements ModelLoader<Uri, Data> {
      private static final Set<String> SCHEMES = Collections.unmodifiableSet(
          new HashSet<>(
              Arrays.asList(
                  "http",
                  "https"
              )
          )
      );
      private final ModelLoader<GlideUrl, Data> urlLoader;
    
      // Public API.
      @SuppressWarnings("WeakerAccess")
      public UrlUriLoader(ModelLoader<GlideUrl, Data> urlLoader) {
        this.urlLoader = urlLoader;
      }
    
      @Override
      public LoadData<Data> buildLoadData(@NonNull Uri uri, int width, int height,
          @NonNull Options options) {
        GlideUrl glideUrl = new GlideUrl(uri.toString());
        return urlLoader.buildLoadData(glideUrl, width, height, options);
      }
    
      @Override
      public boolean handles(@NonNull Uri uri) {
        return SCHEMES.contains(uri.getScheme());
      }
    
      /**
       * Loads {@link java.io.InputStream InputStreams} from {@link android.net.Uri Uris} with http
       * or https schemes.
       */
      public static class StreamFactory implements ModelLoaderFactory<Uri, InputStream> {
    
        @NonNull
        @Override
        public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {
          return new UrlUriLoader<>(multiFactory.build(GlideUrl.class, InputStream.class));
        }
    
        @Override
        public void teardown() {
          // Do nothing.
        }
      }
    }
    

    StreamFactory也是实现一个工厂接口ModelLoaderFactory,具体看下这个接口代码:

    // ModelLoaderFactory.java
    /**
     * An interface for creating a {@link ModelLoader} for a given model type.
     *
     * @param <T> The type of the model the {@link com.bumptech.glide.load.model.ModelLoader}s built by
     *            this factory can handle
     * @param <Y> The type of data the {@link com.bumptech.glide.load.model.ModelLoader}s built by this
     *            factory can load.
     */
    public interface ModelLoaderFactory<T, Y> {
    
      /**
       * Build a concrete ModelLoader for this model type.
       *
       * @param multiFactory A map of classes to factories that can be used to construct additional
       *                     {@link ModelLoader}s that this factory's {@link ModelLoader} may depend on
       * @return A new {@link ModelLoader}
       */
      @NonNull
      ModelLoader<T, Y> build(@NonNull MultiModelLoaderFactory multiFactory);
    
      /**
       * A lifecycle method that will be called when this factory is about to replaced.
       */
      void teardown();
    }
    

    接口比较简单,比如特殊的是还有一个工厂MultiModelLoaderFactory,其实也可以看成一种代理模式,真正去buildLoadData的是通过工厂类StreamFactory构造返回的urlLoader,它能处理的Model类型是GlideUrl,返回的类型是InputStream,而urlloader本身是通过MultiModelLoaderFactory构造:

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

    所以我们总结下下ModelLoader,UrlUriLoader,StreamFactory,ModelLoaderFactory,MultiModelLoaderFactory的关系, 具体的ModelLoader里面有一个代理的ModelLoader,这个代理的ModelLoder由MultiModelLoaderFactory实例化,不管是外层的ModelLoader或者是代理ModelLoader都是由Factory这种方式生成,外层的ModelLoaderFactory在自己的类名下面,而代理ModelLoader统一都由MultiModelLoaderFactory这个工厂生成,类似门面模式。

    ModelLoaderUML.png

    StreamFactory是在Glide初始化的时候注册到ModelLoaderRegistry中:

    //Glide.java
    registry.append(URL.class, InputStream.class, new UrlLoader.StreamFactory())
    

    ModelLoaderRegistry其实只是MultiModelLoaderFactory的外壳,它会把Glide初始化注册的所有ModelFactory保存到MultiModelLoaderFactory:

    // ModelLoaderRegistry.java
    private final MultiModelLoaderFactory multiModelLoaderFactory;
    private final ModelLoaderCache cache = new ModelLoaderCache();
    public synchronized <Model, Data> void append(
          @NonNull Class<Model> modelClass,
          @NonNull Class<Data> dataClass,
          @NonNull ModelLoaderFactory<? extends Model, ? extends Data> factory) {
        multiModelLoaderFactory.append(modelClass, dataClass, factory);
        cache.clear();
      }
    

    按照上面的逻辑,再看一个具体的ModelLoader实现类FileLoader<Data> implements ModelLoader<File, Data>, 实现从File加载Data,这里Data有两个类型java.io.InputStreamjava.io.FileDescriptor,看下具体实现。实际工作会通过内部实例变量FileOpener完成,在FileFetcher loadData中通过FileOpener打开文件,返回需要的数据类型,所以关键就在这个FileOpener,在这里是对文件打开关闭的一个封装接口,根据具体的返回类型构造不同的FileOpener.

    // FileLoader.java
    public class FileLoader<Data> implements ModelLoader<File, Data> {
      private static final String TAG = "FileLoader";
    
      private final FileOpener<Data> fileOpener;
    
      // Public API.
      @SuppressWarnings("WeakerAccess")
      public FileLoader(FileOpener<Data> fileOpener) {
        this.fileOpener = fileOpener;
      }
    
      @Override
      public LoadData<Data> buildLoadData(@NonNull File model, int width, int height,
          @NonNull Options options) {
        return new LoadData<>(new ObjectKey(model), new FileFetcher<>(model, fileOpener));
      }
    
      @Override
      public boolean handles(@NonNull File model) {
        return true;
      }
    
      /**
       * Allows opening a specific type of data from a {@link java.io.File}.
       * @param <Data> The type of data that can be opened.
       */
      public interface FileOpener<Data> {
        Data open(File file) throws FileNotFoundException;
        void close(Data data) throws IOException;
        Class<Data> getDataClass();
      }
    
      private static final class FileFetcher<Data> implements DataFetcher<Data> {
        private final File file;
        private final FileOpener<Data> opener;
        private Data data;
    
        FileFetcher(File file, FileOpener<Data> opener) {
          this.file = file;
          this.opener = opener;
        }
    
        @Override
        public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super Data> callback) {
          try {
            data = opener.open(file);
          } catch (FileNotFoundException e) {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
              Log.d(TAG, "Failed to open file", e);
            }
            callback.onLoadFailed(e);
            return;
          }
          callback.onDataReady(data);
        }
    
        @Override
        public void cleanup() {
          if (data != null) {
            try {
              opener.close(data);
            } catch (IOException e) {
              // Ignored.
            }
          }
        }
    
        @Override
        public void cancel() {
          // Do nothing.
        }
    
        @NonNull
        @Override
        public Class<Data> getDataClass() {
          return opener.getDataClass();
        }
    
        @NonNull
        @Override
        public DataSource getDataSource() {
          return DataSource.LOCAL;
        }
      }
    
      /**
       * Base factory for loading data from {@link java.io.File files}.
       * @param <Data> The type of data that will be loaded for a given {@link java.io.File}.
       */
      public static class Factory<Data> implements ModelLoaderFactory<File, Data> {
        private final FileOpener<Data> opener;
    
        public Factory(FileOpener<Data> opener) {
          this.opener = opener;
        }
    
        @NonNull
        @Override
        public final ModelLoader<File, Data> build(@NonNull MultiModelLoaderFactory multiFactory) {
          return new FileLoader<>(opener);
        }
    
        @Override
        public final void teardown() {
          // Do nothing.
        }
      }
    
    

    FileLoader内部有两个静态内部类StreamFactoryFileDescriptorFactory分别返回两个数据类型java.io.InputStreamjava.io.FileDescriptor。分别构造两种类型的FileOpener,一个返回InputStream类型,另外一个返回ParcelFileDescriptor.

      /**
       * Factory for loading {@link InputStream}s from {@link File}s.
       */
      public static class StreamFactory extends Factory<InputStream> {
        public StreamFactory() {
          super(new FileOpener<InputStream>() {
            @Override
            public InputStream open(File file) throws FileNotFoundException {
              return new FileInputStream(file);
            }
    
            @Override
            public void close(InputStream inputStream) throws IOException {
              inputStream.close();
            }
    
            @Override
            public Class<InputStream> getDataClass() {
              return InputStream.class;
            }
          });
        }
      }
    
      /**
       * Factory for loading {@link ParcelFileDescriptor}s from {@link File}s.
       */
      public static class FileDescriptorFactory extends Factory<ParcelFileDescriptor> {
    
        public FileDescriptorFactory() {
          super(new FileOpener<ParcelFileDescriptor>() {
            @Override
            public ParcelFileDescriptor open(File file) throws FileNotFoundException {
              return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
            }
    
            @Override
            public void close(ParcelFileDescriptor parcelFileDescriptor) throws IOException {
              parcelFileDescriptor.close();
            }
    
            @Override
            public Class<ParcelFileDescriptor> getDataClass() {
              return ParcelFileDescriptor.class;
            }
          });
        }
      }
    

    上面两个Factory也是Glide初始化的时候注册到ModelLoaderRegistry中:

    .append(File.class, InputStream.class, new FileLoader.StreamFactory())
    .append(File.class, ParcelFileDescriptor.class, new FileLoader.FileDescriptorFactory())
    

    2. DataFetcher

    从前面ModelLoader<Model, Data>中有提到过DataFetcher<Data>这个类,这是真正做数据拉取的功能接口,基本上对应不同的ModelLoader会有对应的DataFetcher去实现拉取数据的工作,看下DataFetcher<Data>这个类的接口定义。里面有个内部接口DataCallback,在数据拉取成功后会通过回调接口返回。loadData就是真正发起数据请求的地方,getDataSource返回数据来源,这是个枚举。

    // DataSource.java
    public interface DataFetcher<T> {
    
      /**
       * Callback that must be called when data has been loaded and is available, or when the load
       * fails.
       *
       * @param <T> The type of data that will be loaded.
       */
      interface DataCallback<T> {
    
        /**
         * Called with the loaded data if the load succeeded, or with {@code null} if the load failed.
         */
        void onDataReady(@Nullable T data);
    
        /**
         * Called when the load fails.
         *
         * @param e a non-null {@link Exception} indicating why the load failed.
         */
        void onLoadFailed(@NonNull Exception e);
      }
    
      void loadData(@NonNull Priority priority, @NonNull DataCallback<? super T> callback);
    
    
      void cleanup();
    
      void cancel();
    
      /**
       * Returns the class of the data this fetcher will attempt to obtain.
       */
      @NonNull
      Class<T> getDataClass();
    
      /**
       * Returns the {@link com.bumptech.glide.load.DataSource} this fetcher will return data from.
       */
      @NonNull
      DataSource getDataSource();
    }
    

    看下在Glide中DataFetcher的继承结构,实现类还是很多,可以从AssetPath/Url/File/Uri等地方拉取数据。

    DataFetcher.png

    看下比较常用的HttpUrlFetcher,实现从Url加载返回一个InputStream:

    /**
     * A DataFetcher that retrieves an {@link java.io.InputStream} for a Url.
     */
    public class HttpUrlFetcher implements DataFetcher<InputStream>
    

    loadData方法中,通过loadDataWithRedirects加载url返回result,如果成功就通过onDataReady返回数据:

    // HttpUrlFetcher.java
    InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
    callback.onDataReady(result);
    

    loadDataWithRedirects中通过HttpURLConnection发起网络连接:

    // HttpUrlFetcher.java    
    urlConnection = connectionFactory.build(url);
    for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
       urlConnection.addRequestProperty(headerEntry.getKey(),headerEntry.getValue());
    }
    urlConnection.setConnectTimeout(timeout);
    urlConnection.setReadTimeout(timeout);
    urlConnection.setUseCaches(false);
    urlConnection.setDoInput(true);
    urlConnection.setInstanceFollowRedirects(false);
    
    // Connect explicitly to avoid errors in decoders if connection fails.
    urlConnection.connect();
    // Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352.
    stream = urlConnection.getInputStream();
    

    看下另外几个接口方法的实现,这里DataSource明显就是REMOTE,返回的数据类型是InputStream.class,在cleanup中就是把sream和connection关闭:

      @Override
      public void cleanup() {
        if (stream != null) {
          try {
            stream.close();
          } catch (IOException e) {
            // Ignore
          }
        }
        if (urlConnection != null) {
          urlConnection.disconnect();
        }
        urlConnection = null;
      }
    
      @Override
      public void cancel() {
        isCancelled = true;
      }
    
      @NonNull
      @Override
      public Class<InputStream> getDataClass() {
        return InputStream.class;
      }
    
      @NonNull
      @Override
      public DataSource getDataSource() {
        return DataSource.REMOTE;
      }
    

    3.总结

    再回头看前面提的问题,Glide提供很多ModelLoader接口的实现类来实现不同资源的数据加载,每个ModelLoader不是直接通过构造函数实例化,而是每个Loader内部提供的工厂类进行实例化,在Glide初始化的时候,会把这些工厂类都注册到ModelLoaderRegistry中,构造的时候从里面取出对应的工厂实例化ModelLoaderModelLoader下载数据会通过DataFetcherDataFetcher的实现类基本和ModelLoader对应。

    相关文章

      网友评论

        本文标题:Glide源码分析之数据拉取

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