Glide系列(二) — DataLoadProvider 及与Encoder&Decoder的关系

一、概述

1.1 背景

Glide 是一个图片加载库,跟它同类型的库还有 Picasso、Fresco、Universal-Image-Loader 等。基于 Glide 优秀的缓存管理策略和生命周期关联的特点,目前市面上对 Glide 的使用非常广,因此我们有必要深入研究下 Glide 相关的实现原理,便于更好的使用它。

Glide系列(一) — Glide 框架结构浅析 一文中,我们介绍了 Glide 框架的整体结构,同时也介绍 Glide 中的几个关键概念,其中涉及到 DataLoaderProviderModelLoaderDataFetcher 之间的关系。本文我们重点来分析一下 DataLoaderProvider 及与其相关的 Encoder/Decoder 部分。


Glide 版本: Glide 3.7.0
Github 地址: https://github.com/bumptech/glide/tree/v3.7.0
Gradle 依赖: implementation 'com.github.bumptech.glide:glide:3.7.0'

1.2 系列文章

  1. Glide系列(一) — Glide 框架结构浅析
  2. Glide系列(二) — DataLoadProvider 及与 Encoder&Decoder 的关系
  3. Glide系列(三) — LoadProvider、ModelLoader、DataFetcher 和 ResourceTranscoder 关系
  4. Glide系列(四) — Glide 缓存流程分析
  5. DiskLruCache 源码分析

二、准备知识

在接下去分析之前,我们先要对 Glide 框架的功能有一些基本认识:

  • Glide 框架支持加载静态图(Bitmap)、动态图(Gif)、视频(视频首帧,也是图Bitmap)。
  • Glide 框架接收 DataFetcher 返回的数据类型为 InputStreamFileDescriptor
  • Glide 支持加载的资源路径类型 (即 RequestManager.load(T) 中的T类型) 有:int/Integer、String、File、Uri、URL、byte[] 6种。

本文我们将具体分析 DataLoadProvider 部分,主要从下面2部分展开:

  • DataLoadProvider 注册及使用。
  • Encoder、Decoder 和 DataLoadProvider 的关系。

三、DataLoadProvider

3.1 DataLoadProvider<T, Z> 泛型类型与内部编码器泛型的关系

DataLoadProvider 的主要作用是对外提供相应的编/解码器。根据 Glide系列(一) — Glide 框架结构浅析 中的介绍,当我们知道了 DataLoadProvider<T, Z> 泛型类型时,也就知道了对应编/解码器处理参数的类型。下面举个例子来说明:

StreamBitmapDataLoadProvider<InputStream, Bitmap> 为例:

代码如下:

public class StreamBitmapDataLoadProvider implements DataLoadProvider<InputStream, Bitmap> {
	// 解码 ResultCache、SourceCache 缓存
    public ResourceDecoder<File, Bitmap> getCacheDecoder() {
        return cacheDecoder;
    }
	// 解码 Source 缓存(第5层缓存,即数据源)
    public ResourceDecoder<InputStream, Bitmap> getSourceDecoder() {
        return decoder;
    }
	// 对从 Source 缓存(第5层缓存,即数据源) 获取的数据进行编码
    public Encoder<InputStream> getSourceEncoder() {
        return sourceEncoder;
    }
	// 对经过 transform 之后的数据进行编码
    public ResourceEncoder<Bitmap> getEncoder() {
        return encoder;
    }
}

小结:

  • DataFetcher 是用于加载第5层 Source缓存 的数据 (如网络、Asset目录),并将数据提供给 DataLoadProvider 的 SourceDecoder 进行解码,DataFetcher 对外提供的数据格式有Stream、FileDescriptor两种,这里 StreamBitmapDataLoadProvider 支持接收 DataFetcher 返回 Stream 类型的数据。
  • DataLoadProvider 中解码器:
    • CacheDecoder: 解码 ResultCache、SourceCache 缓存,且这两层的缓存由Glide产生,所以接收File类型的输入,并返回Bitmap 类型的输出。
    • SourceDecoder: 对第5层 Source缓存(数据源) 进行解码,数据源是由 DataFetcher 提供的 Stream 类型。
    • SourceEncoder: 将第5层 Source缓存(数据源) 缓存到第4层 SourceCache缓存
    • Encoder (CacheEncoder): 将第5层的 Source缓存 数据通过 SourceDecoder 解码(生成 Resource),再对解码后的数据使用 transform 操作生成新的 Resource,然后将 新的 Resource 数据缓存到第3层 ResultCache缓存

DataLoadProvider 中几种 编/解码器 的使用场景如下图所示:
在这里插入图片描述

小结:

到这里我们只要知道 DataLoadProvider<T, Z> 不同的泛型类型会影响到内部的编/解码类的接收和返回参数类型就可以了。


在分析完 DataLoadProvider 和其内部 编/解码器 的作用及其使用场景后,接下来我们看下具体有哪些 DataLoadProvider 实例,他们是怎么在 Glide 中查找的,都有些什么作用。

3.2 DataLoadProviderRegistry 注册表

首先,DataLoadProvider 是通过注册机制来维护,注册表为 DataLoadProviderRegistry,其数据结构为一个 HashMap,用来映射 接收参数和返回参数DataLoadProvider 之间的关系。

下面为 DataLoadProviderRegistry 的代码部分,展示了其数据结构和注册部分的逻辑。

// DataLoadProviderRegistry.class
private final Map<MultiClassKey, DataLoadProvider<?, ?>> providers =
        new HashMap<MultiClassKey, DataLoadProvider<?, ?>>();
        
public <T, Z> void register(Class<T> dataClass, 
		Class<Z> resourceClass, DataLoadProvider<T, Z> provider) {
	// 将 dataClass 和 resourceClass 封装成一个 Key 值。
    providers.put(new MultiClassKey(dataClass, resourceClass), provider);
}

小结:

  • DataLoadProviderRegistry 注册表的数据结构是一个 HashMap。
  • 这个 HashMap 中的 key 是由 dataClass 和 resourceClass 两部分的Class类型组成的。而这两部分其实分别对应了 DataLoadProvider<T, Z> 中的两个泛型参数(T、Z)。

3.3 DataLoadProvider 的注册

上面介绍了 DataLoadProviderRegistry 的数据结构,接下来我们来看下具体的注册场景,看看有哪些 DataLoadProvider 类型,以及他们之间的关系如何?

// Glide.class
// DataLoadProvider 注册中心
private final DataLoadProviderRegistry dataLoadProviderRegistry;

Glide(Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, 
		Context context, DecodeFormat decodeFormat) {
	// DataLoadProvider 的注册中心。
    dataLoadProviderRegistry = new DataLoadProviderRegistry();
	
	// 1.处理静态图的DataLoadProvider:输入为InputStream类型,输出为Bitmap类型。
    StreamBitmapDataLoadProvider streamBitmapLoadProvider =
            new StreamBitmapDataLoadProvider(bitmapPool, decodeFormat);
    dataLoadProviderRegistry.register(InputStream.class, Bitmap.class, streamBitmapLoadProvider);

	// 2.处理静态图的DataLoadProvider:输入为Fd类型,输出为Bitmap类型。
    FileDescriptorBitmapDataLoadProvider fileDescriptorLoadProvider =
            new FileDescriptorBitmapDataLoadProvider(bitmapPool, decodeFormat);
    dataLoadProviderRegistry.register(ParcelFileDescriptor.class, Bitmap.class, fileDescriptorLoadProvider);
	// 3.支持处理静态图和视频:是一种包装类型,包含前面两种。
    ImageVideoDataLoadProvider imageVideoDataLoadProvider =
            new ImageVideoDataLoadProvider(streamBitmapLoadProvider, fileDescriptorLoadProvider);
    dataLoadProviderRegistry.register(ImageVideoWrapper.class, Bitmap.class, imageVideoDataLoadProvider);
	// 4.处理Gif的DataLoadProvider:输入为InputStream类型,输出为GifDrawable类型。
    GifDrawableLoadProvider gifDrawableLoadProvider =
            new GifDrawableLoadProvider(context, bitmapPool);
    dataLoadProviderRegistry.register(InputStream.class, GifDrawable.class, gifDrawableLoadProvider);
	// 大杂烩,支持处理静态图、视频和Gif,包含前三种。
    dataLoadProviderRegistry.register(ImageVideoWrapper.class, GifBitmapWrapper.class,
            new ImageVideoGifDrawableLoadProvider(imageVideoDataLoadProvider, 
            		gifDrawableLoadProvider, bitmapPool));
	// 6.这里是支持使用Glide直接下载图片文件。
    dataLoadProviderRegistry.register(InputStream.class, File.class, new StreamFileDataLoadProvider());
}

DataLoadProvider 类型的罗列:

dataClassresourceClassDataLoadProvider说明
InputStreamBitmapStreamBitmapDataLoadProvider处理Stream类型的数据,转成Bitmap。
ParcelFileDescriptorBitmapFileDescriptorBitmapDataLoadProvider处理fd类型的数据,转成Bitmap。
ImageVideoWrapperImageVideoWrapperImageVideoDataLoadProvider处理Stream&fd类型的数据,转成Bitmap。
InputStreamGifDrawableGifDrawableLoadProvider处理Stream类型的数据,转成 GifDrawable。
ImageVideoWrapperGifBitmapWrapperImageVideoGifDrawableLoadProvider处理Stream&fd类型的数据,转成Bitmap或者Gif。
InputStreamFileStreamFileDataLoadProvider用于直接使用Glide下载图片文件。

DataLoadProvider 之间的关系图:
在这里插入图片描述

为什么要设计这么多的包装类型?

实现 Glide 对图片的智能加载。我们在使用 Glide 过程中,如果没有明确使用 asBitmapasGif 方法,那么Glide是不知道我们加载的文件类型,因此就需要考虑所有的情况。使用 DataLoadProvider 包装类时,内部的编解码器也会根据包装类的类型进行编解码器的包装,类似于责任链的调用方式。

小结:

我们在前面第二部分的准备知识里讲过,Glide 可以加载三种类型的数据:静态图、Gif图、视频(首帧),但严格意义上来说,视频首帧其实也可以归类到前面的静态图类型。结合上面的表格,我们可以得到以下结论。

  1. Glide 支持加载两种图:静态图、动态图。
  2. dataClass 输入类型只有两种: InputStream、ParcelFileDescriptor,这与我们前面介绍的 DataFetcher 返回的数据类型保持一致。
  3. dataClass 输入类型为 ImageVideoWrapper的,它里面包装了 InputStream 和 ParcelFileDescriptor
  4. resourceClass 返回的类型只有3种:Bitmap、GifDrawable、File,其中前两种与图片显示有关,最后一种是下载图片文件。
  5. resourceClass 返回的类型为 GifBitmapWrapper 的,它里面包装了 Bitmap 和 GifDrawable 两种类型。

3.4 DataLoadProvider 的获取

介绍完了 DataLoadProvider 的注册之后,下面我们看下 DataLoadProvider 的获取方法。

// Glide.class
<T, Z> DataLoadProvider<T, Z> buildDataProvider(Class<T> dataClass, Class<Z> decodedClass) {
    return dataLoadProviderRegistry.get(dataClass, decodedClass);
}

// DataLoadProviderRegistry.class
public <T, Z> DataLoadProvider<T, Z> get(Class<T> dataClass, Class<Z> resourceClass) {
    DataLoadProvider<?, ?> result;
    // GET_KEY 是一个单例,对单例进行赋值时需要上锁。
    synchronized (GET_KEY) {
        GET_KEY.set(dataClass, resourceClass);
        result = providers.get(GET_KEY);
    }
    if (result == null) {
        result = EmptyDataLoadProvider.get();
    }
    return (DataLoadProvider<T, Z>) result;
}

小结:

在其它地方调用 Glide.buildDataProvider() 方法,通过 dataClass、resourceClass 构建的 MultiClassKey 来从注册表里获取匹配的 DataLoadProvider。


3.5 示例代码

下面是调用 Glide.buildDataProvider() 的场景。

// DrawableTypeRequest.class
DrawableTypeRequest(Class<ModelType> modelClass, ModelLoader<ModelType, InputStream> streamModelLoader,
        ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader, Context context, Glide glide,
        RequestTracker requestTracker, Lifecycle lifecycle, RequestManager.OptionsApplier optionsApplier) {
    super(context, modelClass,
    		// 这里传入的 resourceClass 是 GifBitmapWrapper 类型。
            buildProvider(glide, streamModelLoader, fileDescriptorModelLoader, GifBitmapWrapper.class,
                    GlideDrawable.class, null),
            glide, requestTracker, lifecycle);
    this.streamModelLoader = streamModelLoader;
    this.fileDescriptorModelLoader = fileDescriptorModelLoader;
    this.optionsApplier = optionsApplier;
}

private static <A, Z, R> FixedLoadProvider<A, ImageVideoWrapper, Z, R> buildProvider(Glide glide, 
	    ModelLoader<A, InputStream> streamModelLoader,
        ModelLoader<A, ParcelFileDescriptor> fileDescriptorModelLoader, 
        Class<Z> resourceClass,
        Class<R> transcodedClass,
        ResourceTranscoder<Z, R> transcoder) {
	// 省略代码
	// 此处dataClass=ImageVideoWrapper,且resourceClass=GifBitmapWrapper,
	// 所以返回的是ImageVideoGifDrawableLoadProvider。
    DataLoadProvider<ImageVideoWrapper, Z> dataLoadProvider = 
    			glide.buildDataProvider(ImageVideoWrapper.class, resourceClass);
    // 省略代码
    // 然后统一包装到FixedLoadProvider中。
    return new FixedLoadProvider<A, ImageVideoWrapper, Z, R>(modelLoader, transcoder, dataLoadProvider);
}

小结: 熟悉下面几种请求类型对应的 DataLoadProvider。

请求类型dataClassresourceClassDataLoadProvider
BitmapTypeRequestImageVideoWrapperImageVideoWrapperImageVideoDataLoadProvider
GifTypeRequestInputStreamGifDrawableGifDrawableLoadProvider
DrawableTypeRequestImageVideoWrapperGifBitmapWrapperImageVideoGifDrawableLoadProvider
GenericTranscodeRequestInputStreamFileStreamFileDataLoadProvider

到这里,我们就分析完了 DataLoadProvider 部分,接下来我们再分析一下 DataLoadProvider 中的编解码,以及他们与 DataLoadProvider 实现类的对应关系。


四、Encoder & ResourceEncoder

4.1 Encoder 的类型介绍

关于 Encoder & ResourceEncoder 的所有类型如下图所示:
在这里插入图片描述
ImageVideoWrapperEncoder 类型关系图:
在这里插入图片描述

Encoder 类型主要功能
NullEncoder空实现
StreamEncoder将 InputStream 写入文件。
ImageVideoWrapperEncoder包含了StreamEncoder 和 FileDescriptorEncoder,是一个包装类,用于判断执行哪种类型的Encoder进行文件保存。
BitmapEncoder将 Bitmap 保存为文件
GifResourceEncoder将 GifDrawable 保存为文件
GifBitmapWrapperResourceEncoder包含有 BitmapEncoder 和 GifEncoder,是一个包装类,用于判断执行哪种类型的Encoder进行文件保存。

小结:

  • Encoder 分两种类型,一种是直接继承自 Encoder,另一种继承自 ResourceEncoder。
  • 继承自 Encoder 的实现类,其 encode() 方法接收任意类型的参数,一般作为 DataLoadProvider 的 SourceEncoder 参数。
  • 继承自 ResourceEncoder 的实现类,其 encode() 方法中接收 Resource 类型的参数,一般作为 DataLoadProvider 的 CacheEncoder 参数。
  • ImageVideoWrapperEncoder 是一个包装类型,里面包含有StreamEncoder 和 FileDescriptorEncoder 。
  • GifBitmapWrapperResourceEncoder 是一个包装类型,里面包含有 BitmapEncoder(实际为 ImageVideoWrapperEncoder 类型) 和 GifEncoder。
  • 包装类型的 Encoder 都是为了通过条件判断执行对应的 encode 逻辑。

4.2 Encoder 与 DataLoadProvider 的关系

下图为 DataLoadProvider 与 编码类型的关联关系。
在这里插入图片描述

DataLoadProvider 类型SourceEncoder (Encoder)CacheEncoder (ResourceEncoder)
StreamBitmapDataLoadProviderStreamEncoderBitmapEncoder
FileDescriptorBitmapDataLoadProviderNullEncoderBitmapEncoder
ImageVideoDataLoadProviderImageVideoWrapperEncoderBitmapEncoder
GifDrawableLoadProviderStreamEncoderGifResourceEncoder
ImageVideoGifDrawableLoadProviderImageVideoWrapperEncoderGifBitmapWrapperResourceEncoder
StreamFileDataLoadProvider(在上图中没有体现)StreamEncoder/

五、ResourceDecoder

5.1 ResourceDecoder 的类型介绍

关于 ResourceDecoder 的所有类型如下图所示:
在这里插入图片描述

Decoder 类型主要功能
FileDecoder返回一个包含File的 FileResource 对象
FileDescriptorBitmapDecoder将一个fd指向的文件转换为Resource
FileToStreamDecoder将输入的 File 类型转换为 Stream 传递给下一个Decoder
GifResourceDecoder将输入数据转换为一个GifDrawableResource对象
GifBitmapWrapperResourceDecoder包含有 BitmapDecoder 和 GifDecoder,是一个包装类,用于判断执行哪种类型的Decoder进行解码。
StreamBitmapDecoder将InputStream的输入转化为Bitmap
ImageVideoBitmapDecoder包含了StreamDecoder 和 FileDescriptoDecoder,是一个包装类,用于判断执行哪种类型的Decoder进行解码。
GifBitmapWrapperStreamResourceDecoder将输入的 InputStream 类型转换为 ImageVideoWrapper 传递给下一个Decoder

小结:

  • Decoder 最基础的解码器:FileDescriptorBitmapDecoder 、GifResourceDecoder 、StreamBitmapDecoder,解析出可用的数据类型。
  • Decoder 输入类型转换器:FileToStreamDecoder(File->Stream) 、GifBitmapWrapperStreamResourceDecoder,即将输入的参数类型转换为另一种参数类型。
  • ImageVideoBitmapDecoder 是一个包装类型,里面包含有StreamDecoder 和 FileDescriptorDecoder 。
  • GifBitmapWrapperResourceDecoder 是一个包装类型,里面包含有 BitmapDecoder(实际为 ImageVideoBitmapDecoder 类型) 和 GifDecoder。
  • 包装类型的 Decoder 都是为了通过条件判断执行对应的 decode 逻辑。

5.2 ResourceDecoder 与 DataLoadProvider 的关系

下图为 DataLoadProvider 与 解码码类型的关联关系。
在这里插入图片描述

DataLoadProvider 类型SourceDecoderCacheDecoder
StreamBitmapDataLoadProviderStreamBitmapDecoderFileToStreamDecoder -> StreamBitmapDecoder
FileDescriptorBitmapDataLoadProviderFileDescriptorBitmapDecoderFileToStreamDecoder -> StreamBitmapDecoder
ImageVideoDataLoadProviderImageVideoBitmapDecoderFileToStreamDecoder -> StreamBitmapDecoder
GifDrawableLoadProviderGifResourceDecoderFileToStreamDecoder -> GifResourceDecoder
ImageVideoGifDrawableLoadProviderGifBitmapWrapperResourceDecoderFileToStreamDecoder
-> GifBitmapWrapperStreamResourceDecoder
-> GifBitmapWrapperResourceDecoder
StreamFileDataLoadProvider(在上图中没有体现)/FileDecoder

小结:

  1. 图中的 GifBitmapWrapperResourceDecoder 解码器内的逻辑全部只执行 SourceDecoder 部分 (即蓝色部分的流程),不执行 CacheDecoder 部分的逻辑。
  2. 图中深绿色部分代表DataLoadProvider 类型。其用虚线引入解码类型的节点,表示下面的 SourceDecoder 和 CacheDecoder 是该 DataLoadProvider 内的两种解码器。

六、总结

  1. 了解 DataLoadProvider<T, Z> 泛型类型与内部编码器的关系。
  2. 了解编/解码器在缓存的哪个阶段使用 (看上面的图)。
  3. 了解 DataLoadProvider 注册表的数据结构 (HashMap),注册的 DataLoadProvider 类型。
  4. 了解注册的几个 DataLoadProvider 的包含关系 (看上面的图)。
  5. 了解不同的 Request 与各组 DataLoadProvider 的对应关系 (看上面的表格)。
  6. 了解 Encoder 的种类及其各自的作用 (看上面的表格)。
  7. 了解不同 Encoder 与 DataLoadProvider 的对应关系 (看上面的图)。
  8. 了解 Decoder 的种类及其各自的作用 (看上面的表格)。
  9. 了解不同 Decoder 与 DataLoadProvider 的对应关系 (看上面的图)。
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值