Glide系列(三) — LoadProvider、ModelLoader、DataFetcher和ResourceTranscoder关系

一、概述

1.1 背景

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

Glide系列(一) — Glide 框架结构浅析 一文中,我们介绍了 Glide 框架的整体结构,同时也介绍 Glide 中的几个关键概念,其中涉及到 DataLoaderProviderModelLoaderDataFetcher 之间的关系。在上一篇文章 Glide系列(二) — DataLoadProvider 及与 Encoder&Decoder 的关系 中我们分析了 DataLoaderProvider 及其 Encoder&Decoder 的相关内容。本文我们重点来分析一下 LoadProvider、ModelLoader、DataFetcher、ResourceTranscoder 部分。

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种。

在开始分析之前,我们先来回顾下 LoadProvider、ModelLoader、DataFetcher、ResourceTranscoder 之间的关系。

LoadProvider、ModelLoader、DataFetcher、ResourceTranscoder 之间的关系:
在这里插入图片描述

本文我们将从下面几个方面进行分析:

  1. LoadProvider 是什么,有什么作用?
  2. ModelLoader 是什么,有什么作用?
  3. DataFetcher 是什么,有什么作用?
  4. ResourceTranscoder 是什么,有什么作用?

三、LoadProvider

在第二部分的图中,我们可以知道了 LoadProvider 是一个聚合类。它继承自 DataLoadProvider,同时与 ModelLoader 和 ResourceTranscoder 关联,具备了对一个原始数据操作的所有能力(数据加载 -> 数据解码 -> 数据转换)。

通过查看源码,我们知道 LoadProvider 有两个子类:FixLoadProviderChildLoadProvider。其类的关系图如下所示:

LoadProvider 类的关系图如下:
在这里插入图片描述

3.1 FixLoadProvider

FixLoadProvider

public class FixedLoadProvider<A, T, Z, R> implements LoadProvider<A, T, Z, R>  {
    private final ModelLoader<A, T> modelLoader;
    private final ResourceTranscoder<Z, R> transcoder;
    private final DataLoadProvider<T, Z> dataLoadProvider;

    public FixedLoadProvider(ModelLoader<A, T> modelLoader, ResourceTranscoder<Z, R> transcoder,
            DataLoadProvider<T, Z> dataLoadProvider) {
        this.modelLoader = modelLoader;
        this.transcoder = transcoder;
        this.dataLoadProvider = dataLoadProvider;
    }
}

小结:

FixLoadProvider 是一个聚合类,持有 ModelLoader、ResourceTranscoder、DataLoadProvider 对象的引用,具备提供 DataFetcher、图片编/解码器、图片转码 的功能。
它的创建场景在各个 Request 构造方法中,通过传入的 dataClass、resourceClass、transcodedClass 的类型构造处对应的 DataLoadProvider、Transcoder 和 ModelLoader,并将它们封装到一起。

3.2 ChildLoadProvider

public class ChildLoadProvider<A, T, Z, R> implements LoadProvider<A, T, Z, R>, Cloneable {
    private final LoadProvider<A, T, Z, R> parent;

    private ResourceDecoder<File, Z> cacheDecoder;

    public ChildLoadProvider(LoadProvider<A, T, Z, R> parent) {
        this.parent = parent;
    }
    @Override
    public ModelLoader<A, T> getModelLoader() {
        return parent.getModelLoader();
    }
    
    public void setCacheDecoder(ResourceDecoder<File, Z> cacheDecoder) {
        this.cacheDecoder = cacheDecoder;
    }
    
    @Override
    public ResourceDecoder<File, Z> getCacheDecoder() {
        if (cacheDecoder != null) {
            return cacheDecoder;
        } else {
            return parent.getCacheDecoder();
        }
    }
}

小结:

ChildLoadProvider 内持有一个 LoadProvider 引用,相关的操作都是通过这个 LoadProvider 引用进行操作,同时提供了对 编解码器 进行赋值的 Api,所以在加载图片时,让使用自定义的 Encoder、Decoder、ResourceTranscoder 成为可能。

设置自定义的解码器方法如下所示:

Glide.with(this)
     .load("https://")
     .asBitmap()
     .decoder(object : ResourceDecoder<ImageVideoWrapper, Bitmap> {
         override fun getId(): String { //todo }

         override fun decode(source: ImageVideoWrapper?, 
         		width: Int, height: Int): Resource<Bitmap> {
             //todo
         }
     })
	.into(ImageView(this))

3.3 小结

  • FixLoadProvider 和 ChildLoadProvider 构成了一个装饰者模式。其中 ChildLoadProvider 是 Decorator 角色,他提供设置 编/解码器的功能。
  • 为什么要这么设计? 默认情况下 FixLoadProvider 是自动创建的,不支持用户设置自定义的ResourceTranscoder 以及编/解码器。而通过装饰者模式可以对原有的 FixLoadProvider 功能进行增强,使之具备这个功能。

到这里 LoaderProvider 的内容就介绍完了。在分析 ModelLoader 之前,我们先看下通过 ModelLoader 获取的 DataFetcher,这有助于我们后面分析 ModelLoader


四、DataFetcher

Glide系列(一) — Glide 框架结构浅析 中,我们知道 DataFetcher 是用来加载第5层缓存中的数据。下面我们看下 DataFetcher 的子类类型,以及这些 DataFetcher 子类都返回些什么类型的数据。

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

DataFetcher 类型resourceClass获取方法
ByteArrayFetcherbyte[]InputStream
HttpUrlFetcherInputStream通过 Url 获取网络端数据。
ImageVideoFetcher(包装类)ImageVideoWrapper包装类型,返回的 ImageVideoWrapper 中包含 InputStream 和 ParcelFileDescriptor 两种类型 。
StreamAssetPathFetcherInputStream通过 AssetManager.open(String path) 访问本地数据,返回ParcelFileDescriptor。
FileDescriptorAssetPathFetcherParcelFileDescriptor通过 AssetManager.openFd(path).getParcelFileDescriptor() 访问本地数据,返回 ParcelFileDescriptor。
StreamLocalUriFetcherInputStream通过 ContentResolver.openInputStream(Uri) 访问本地数据,返回 InputStream 。
FileDescriptorLocalUriFetcherParcelFileDescriptor通过 ContentResolver.openAssetFileDescriptor(Uri) 访问本地数据,返回 ParcelFileDescriptor。

小结:

  • DataFetcher 用于获取第5层的缓存数据(数据源),第5层的缓存分为本地端和网络端。
    • 网络端:通过 Url 方式获取数据,依托于网络请求。
    • 本地端:文件存储在SDCard上,assets/drawable目录上,可以通过 File对象,文件路径(String),文件Fd,Uri 等方式获取。
  • DataFetcher 返回的数据类型只有 两种InputStream、ParcelFileDescriptor
  • 本地数据的访问方式:通过 File、ContentResolver、AssetManager 等方式进行访问。

到这里,DataFetcher 的内容就介绍完了。下面我们来分析另一个重要的类 ModelLoader

五、ModelLoader

5.1 准备知识

ModelLoader<T, Y> 与 DataLoadProvider<T, Z> 的设计思想很相似,都是通过泛型类型从注册表里查找对应的 DataFetcher<Y>对象。

ModelLoader 的接口定义如下:

public interface ModelLoader<T, Y> {
	// 根据传入的数据类型T,查找对应的数据加载器DataFetcher<Y>,且加载返回的数据为Y。
    DataFetcher<Y> getResourceFetcher(T model, int width, int height);
}

为了便于理解,在分析 ModelLoader 之前我们先来看看下面几个问题:

  • modelClass、resourceClass 分别是什么?
    • modelClass 是请求时传入的参数类型。
    • resourceClass 是希望 DataFetcher 返回的参数类型,上面分析过了,只有 InputStream、ParcelFileDescriptor 两种返回类型。
  • modelClass 的种类有哪些,从哪里传入的?
    • modelClass 的类型是通过 RequestManager.load(T t) 方法的 T 类型传入的。
    • modelClass 的种类:int/Integer、String、File、Uri、URL、byte[] 6种参数类型,其中 byte[] 类型为实际的图片数据。
  • resourceClass 的种类有哪些,从哪里传入的?

有了上面的准备知识之后,下面我们来分析下 ModelLoader 注册模块的逻辑。先来看下 ModelLoader 的注册表的数据结构。

5.2 GenericLoaderFactory 注册表

// GenericLoaderFactory.class
// 存放的是构造 ModelLoader 的工厂类。
Map<Class/*T*/, Map<Class/*Y*/, ModelLoaderFactory/*T, Y*/>> modelClassToResourceFactories =
        new HashMap<Class, Map<Class, ModelLoaderFactory>>();
// 缓存之前创建过的 ModelLoader。
Map<Class/*T*/, Map<Class/*Y*/, ModelLoader/*T, Y*/>> cachedModelLoaders =
        new HashMap<Class, Map<Class, ModelLoader>>();
            
public synchronized <T, Y> ModelLoaderFactory<T, Y> register(Class<T> modelClass, 
		Class<Y> resourceClass, ModelLoaderFactory<T, Y> factory) {
    cachedModelLoaders.clear();
    
	// 1.先根据 modelClass 从 resourceToFactories 中获取一个 Map。
    Map<Class, ModelLoaderFactory> resourceToFactories = modelClassToResourceFactories.get(modelClass);
    if (resourceToFactories == null) {
        resourceToFactories = new HashMap<Class/*Y*/, ModelLoaderFactory/*T, Y*/>();
        modelClassToResourceFactories.put(modelClass, resourceToFactories);
    }
	// 2.将 resourceClass 作为内置 HashMap 的 Key,将ModelLoaderFactory 作为vlaue 存入。
    ModelLoaderFactory/*T, Y*/ previous = resourceToFactories.put(resourceClass, factory);
    // ...省略代码...

    return previous;
}

ModelLoader 注册表的数据结构:
在这里插入图片描述

小结:

  • GenericLoaderFactory 中有两个缓存池,且都是嵌套 HashMap结构 (如下图所示)。
    • modelClassToResourceFactories 用于缓存 ModelLoaderFactory 对象。
    • cachedModelLoaders 用于缓存已构建的 ModelLoader 对象。
  • 这两个缓存池的外层都使用 modelClass 作为 Key 进行存储,内层使用 resourceClass 作为 Key 进行存储 (5.3节会列出对应关系)。
    • 外层以请求传入的参数 (modelClass) 类型维度划分,有如下几种:int/Integer、String、File、Uri、URL、byte[]
    • 内层以 resourceClass 类型为 Key,所以只有 InputStream、ParcelFileDescriptor 两种返回类型。

5.3 ModelLoader 的注册流程

// Glide.class
private final GenericLoaderFactory loaderFactory;

Glide(Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, 
			Context context, DecodeFormat decodeFormat) {
	// GenericLoaderFactory是ModelLoader注册表。		
	loaderFactory = new GenericLoaderFactory(context);
	// 第一个参数与RequestManager.load(T t)请求类型一一对应,只有6种。
	// 第二个参数与DataFetcher返回类型一一对应,只有2种。
	register(File.class, ParcelFileDescriptor.class, new FileDescriptorFileLoader.Factory());
    register(File.class, InputStream.class, new StreamFileLoader.Factory());
    register(int.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());
    register(int.class, InputStream.class, new StreamResourceLoader.Factory());
    register(Integer.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());
    register(Integer.class, InputStream.class, new StreamResourceLoader.Factory());
    register(String.class, ParcelFileDescriptor.class, new FileDescriptorStringLoader.Factory());
    register(String.class, InputStream.class, new StreamStringLoader.Factory());
    register(Uri.class, ParcelFileDescriptor.class, new FileDescriptorUriLoader.Factory());
    register(Uri.class, InputStream.class, new StreamUriLoader.Factory());
    register(URL.class, InputStream.class, new StreamUrlLoader.Factory());
    register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());
    register(byte[].class, InputStream.class, new StreamByteArrayLoader.Factory());
}

public <T, Y> void register(Class<T> modelClass, Class<Y> resourceClass, ModelLoaderFactory<T, Y> factory) {
    ModelLoaderFactory<T, Y> removed = loaderFactory.register(modelClass, resourceClass, factory);
    if (removed != null) {
        removed.teardown();
    }
}

小结:

  • 上面 ModelLoader 注册之后,modelClass、resourceClass 与 ModelLoaderFactory 映射关系如下图所示。
  • modelClass 类型有6种:int/Integer、String、File、Uri、URL、byte[]
  • resourceClass 类型有2种:InputStream、ParcelFileDescriptor

在这里插入图片描述


5.4 ModelLoader 的获取流程

// Glide.class
public static <T, Y> ModelLoader<T, Y> buildModelLoader(Class<T> modelClass, 
		Class<Y> resourceClass, Context context) {
    // 从 ModelLoader 注册表里获取指定的 ModelLoader。
    return Glide.get(context).getLoaderFactory().buildModelLoader(modelClass, resourceClass);
}

// GenericLoaderFactory.class
public synchronized <T, Y> ModelLoader<T, Y> buildModelLoader(Class<T> modelClass, 
		Class<Y> resourceClass) {
	// 1.尝试从cachedModelLoaders缓存里取。
    ModelLoader<T, Y> result = getCachedLoader(modelClass, resourceClass);
    if (result != null) {
        if (NULL_MODEL_LOADER.equals(result)) {
            return null;
        } else {
        	// 2.获取到缓存的 ModelLoader 后直接返回。
            return result;
        }
    }
	// 3.尝试从 modelClassToResourceFactories 缓存中获取构造 ModelLoader 的工厂类。
    final ModelLoaderFactory<T, Y> factory = getFactory(modelClass, resourceClass);
    if (factory != null) {
    	// 4.进行 ModelLoader 对象创建。
        result = factory.build(context, this);
        // 5.将创建的 ModelLoader 对象保存到cachedModelLoaders缓存中。
        cacheModelLoader(modelClass, resourceClass, result);
    } else {
        cacheNullLoader(modelClass, resourceClass);
    }
    return result;
}

小结: 根据 modelClass 和 resourceClass 从注册表查找指定的 ModelLoader 分4个步骤:

  • step1:尝试从 cachedModelLoaders 缓存里取,如果获取到缓存的 ModelLoader 后则直接返回。
  • step2:如果step1 没获取到 ModelLoader,则尝试从 modelClassToResourceFactories 缓存中获取构造 ModelLoader 的工厂类。
  • step3:执行 step2 获取到的工厂类,进行 ModelLoader 对象创建。
  • step4:将 step3 创建的 ModelLoader 对象保存到 cachedModelLoaders 缓存中。

5.5 ModelLoader 与 DataFetcher 的关系

因为 RequestManager.load(T model) 方法的参数类型与我们注册时的 modelClass 类型是保持一致的。所以在分析 ModelLoader 与 DataFetcher 的关系之前,我们先来看下RequestManager.load(T model) 方法是如何查询 ModelLoader 的。

// RequestManager.class
public DrawableTypeRequest<File> load(File file);
public DrawableTypeRequest<Integer> load(Integer resourceId);
public DrawableTypeRequest<String> load(String string);
public DrawableTypeRequest<Uri> load(Uri uri)
public DrawableTypeRequest<URL> load(URL url)
public <T> DrawableTypeRequest<T> load(T model)
public DrawableTypeRequest<byte[]> load(byte[] model);

// 下面以String和Uri为例来说明。
public DrawableTypeRequest<String> fromString() {
    return loadGeneric(String.class);
}

public DrawableTypeRequest<Uri> load(Uri uri) {
    return (DrawableTypeRequest<Uri>) fromUri().load(uri);
}

public DrawableTypeRequest<Uri> fromUri() {
    return loadGeneric(Uri.class);
}
// 以 String 类型的 modelClass 为例。
private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {
	// 1.查找 StreamModelLoader
    ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);
    // 2.查找FileDescriptorModelLoader
    ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader =
            Glide.buildFileDescriptorModelLoader(modelClass, context);
	// 3.将查找到的 StreamModelLoader和FileDescriptorModelLoader 关联到DrawableTypeRequest中。
    return optionsApplier.apply(
            new DrawableTypeRequest<T>(modelClass, streamModelLoader, fileDescriptorModelLoader, context,
                    glide, requestTracker, lifecycle, optionsApplier));
}

下图为 StreamModelLoader 调用流程图。
在这里插入图片描述

下图为 FileDescriptorModelLoader 调用流程图。
在这里插入图片描述

小结:

ModelLoader 的调用流程使用了责任链模式,每个 ModelLoader 内持有下一个 ModelLoader 对象。如果当前的 ModelLoader 无法处理给定的数据类型,就交给下一个 ModelLoader 去处理,直到找到可以处理该数据类型的 ModelLoader 为止,并返回该 ModelLoader 持有的 DataFetcher 对象。

5.6 小结

  1. 了解 LoadProvider 的作用,及两个子类的差异。
  2. 了解 DataFetcher 的作用,及其返回值类型的种类 (两种)。
  3. 了解 ModelLoader 泛型参数对 DataFetcher 的影响,及其责任链调用流程。
  4. 了解 ModelLoader 注册表的数据结构及其存/取流程。
  5. 了解 ModelLoader 与 DataFetcher 的关系。

到这里关于 ModelLoader 就分析完了,下面分析最后一个与 ModelLoader 关联的部分 (ResourceTranscoder)。


六、ResourceTranscoder

ResourceTranscoder 的作用主要是对数据的类型进行转换,它的作用与 Transform 类似(都是对数据进行操作),只是Transform 不能改变数据类型。

6.1 TranscoderRegistry 注册表

// TranscoderRegistry.class
private final Map<MultiClassKey, ResourceTranscoder<?, ?>> factories =
        new HashMap<MultiClassKey, ResourceTranscoder<?, ?>>();
            
public <Z, R> void register(Class<Z> decodedClass, Class<R> transcodedClass, 
			ResourceTranscoder<Z, R> transcoder) {
    factories.put(new MultiClassKey(decodedClass, transcodedClass), transcoder);
}

小结:

与 DataLoadProvider 注册方法类似,这里是将 decodedClass、transcodedClass 封装成一个 MultiClassKey 对象作为 Key,存放在 HashMap 中。


6.2 ResourceTranscoder 的注册流程

// Glide.class
// Transcoder 转码器注册中心
private final TranscoderRegistry transcoderRegistry = new TranscoderRegistry();
    
Glide(Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, 
		Context context, DecodeFormat decodeFormat) {
    transcoderRegistry.register(Bitmap.class, GlideBitmapDrawable.class,
            new GlideBitmapDrawableTranscoder(context.getResources(), bitmapPool));
    transcoderRegistry.register(GifBitmapWrapper.class, GlideDrawable.class,
            new GifBitmapWrapperDrawableTranscoder(
                    new GlideBitmapDrawableTranscoder(context.getResources(), bitmapPool)));
}

小结: TranscoderRegistry 注册表中 ResourceTranscoder 之间的关系。
在这里插入图片描述


6.3 ResourceTranscoder 的获取流程

// Glide.class
<Z, R> ResourceTranscoder<Z, R> buildTranscoder(Class<Z> decodedClass, 
		Class<R> transcodedClass) {
    return transcoderRegistry.get(decodedClass, transcodedClass);
}

// DrawableTypeRequest.class
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) {
	// 省略代码
	if (transcoder == null) {
		// 此处传入的resourceClass是GifBitmapWrapper类型,transcodedClass是GlideDrawable类型,
		// 因此匹配到的是 GifBitmapWrapperDrawableTranscoder。
        transcoder = glide.buildTranscoder(resourceClass, transcodedClass);
    }
    // 省略代码
    return new FixedLoadProvider<A, ImageVideoWrapper, Z, R>(modelLoader, transcoder, dataLoadProvider);
}
请求类型decodedClass/transcodedClassResourceTranscoder
BitmapTypeRequestBitmapBitmapUnitTranscoder
GifTypeRequestGifDrawableGifDrawableUnitTranscoder
DrawableTypeRequestGifBitmapWrapperGlideDrawable
GenericTranscodeRequest//UnitTranscoder

小结:

  • ResourceTranscoder 的注册与 DataLoadProvider 类似,只是使用的参数类型不同,ResourceTranscoder 是通过 decodedClass, transcodedClass 参数类型作为 Key 来进行映射的 。
  • 在不同类型的 Request 构建过程中,传入对应的decodedClass, transcodedClass 类型进行匹配。
  • ResourceTranscoder 的作用与 Transform 类似,都是对数据进行操作,只是 ResourceTranscoder 可以改变返回的数据类型,而Transform 不能改变返回的数据类型。
  • 如果 decodedClass, transcodedClass 类型一致,即表示转换前后类型没有发生改变,就使用UnitTranscoder进行透传。

七、总结

  1. 了解 LoadProvider 的作用,及两个子类的差异。
  2. 了解 DataFetcher 的作用,及其返回值类型的种类 (两种)。
  3. 了解 ModelLoader 泛型参数对 DataFetcher 的影响,及其责任链调用流程。
  4. 了解 ModelLoader 注册表的数据结构及其存/取流程。
  5. 了解 ModelLoader 与 DataFetcher 的关系。
  6. 了解 ResourceTranscoder 的作用,即数据类型转换,与 Transform 类似 。
  7. 了解不同类型 Request 对应的 ResourceTranscoder 类型。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值