一、概述
1.1 背景
Glide 是一个图片加载库,跟它同类型的库还有 Picasso、Fresco、Universal-Image-Loader 等。基于 Glide 优秀的缓存管理策略和生命周期关联的特点,目前市面上对 Glide 的使用非常广,因此我们有必要深入研究下 Glide 相关的实现原理,便于更好的使用它。
在 Glide系列(一) — Glide 框架结构浅析 一文中,我们介绍了 Glide 框架的整体结构,同时也介绍 Glide 中的几个关键概念,其中涉及到 DataLoaderProvider
、ModelLoader
和 DataFetcher
之间的关系。在上一篇文章 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 系列文章
- Glide系列(一) — Glide 框架结构浅析
- Glide系列(二) — DataLoadProvider 及与 Encoder&Decoder 的关系
- Glide系列(三) — LoadProvider、ModelLoader、DataFetcher 和 ResourceTranscoder 关系
- Glide系列(四) — Glide 缓存流程分析
- DiskLruCache 源码分析
二、准备知识
在接下去分析之前,我们先要对 Glide 框架的功能有一些基本认识:
- Glide 框架支持加载静态图
(Bitmap)
、动态图(Gif)
、视频(视频首帧,也是图Bitmap
)。- Glide 框架接收 DataFetcher 返回的数据类型为
InputStream
、FileDescriptor
。- Glide 支持加载的资源路径类型 (即
RequestManager.load(T)
中的T类型) 有:int/Integer、String、File、Uri、URL、byte[]
6种。
在开始分析之前,我们先来回顾下 LoadProvider、ModelLoader、DataFetcher、ResourceTranscoder
之间的关系。
LoadProvider、ModelLoader、DataFetcher、ResourceTranscoder 之间的关系:
本文我们将从下面几个方面进行分析:
- LoadProvider 是什么,有什么作用?
- ModelLoader 是什么,有什么作用?
- DataFetcher 是什么,有什么作用?
- ResourceTranscoder 是什么,有什么作用?
三、LoadProvider
在第二部分的图中,我们可以知道了 LoadProvider 是一个聚合类。它继承自 DataLoadProvider,同时与 ModelLoader 和 ResourceTranscoder 关联,具备了对一个原始数据操作的所有能力(
数据加载 -> 数据解码 -> 数据转换
)。
通过查看源码,我们知道 LoadProvider 有两个子类:FixLoadProvider
、ChildLoadProvider
。其类的关系图如下所示:
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 | 获取方法 |
---|---|---|
ByteArrayFetcher | byte[] | InputStream |
HttpUrlFetcher | InputStream | 通过 Url 获取网络端数据。 |
ImageVideoFetcher(包装类) | ImageVideoWrapper | 包装类型,返回的 ImageVideoWrapper 中包含 InputStream 和 ParcelFileDescriptor 两种类型 。 |
StreamAssetPathFetcher | InputStream | 通过 AssetManager.open(String path) 访问本地数据,返回ParcelFileDescriptor。 |
FileDescriptorAssetPathFetcher | ParcelFileDescriptor | 通过 AssetManager.openFd(path).getParcelFileDescriptor() 访问本地数据,返回 ParcelFileDescriptor。 |
StreamLocalUriFetcher | InputStream | 通过 ContentResolver.openInputStream(Uri) 访问本地数据,返回 InputStream 。 |
FileDescriptorLocalUriFetcher | ParcelFileDescriptor | 通过 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
的种类有哪些,从哪里传入的?
resourceClass
代表实际 DataFetcher 返回的数据,只有InputStream、ParcelFileDescriptor
两种返回类型。resourceClass
的类型会在构造各自的 Request 时传入。这部分内容在 Glide系列(二) — DataLoadProvider 及与 Encoder&Decoder 的关系 的第3.5章节中有提到。
有了上面的准备知识之后,下面我们来分析下 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 小结
- 了解 LoadProvider 的作用,及两个子类的差异。
- 了解 DataFetcher 的作用,及其返回值类型的种类 (两种)。
- 了解 ModelLoader 泛型参数对 DataFetcher 的影响,及其责任链调用流程。
- 了解 ModelLoader 注册表的数据结构及其存/取流程。
- 了解 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/ | transcodedClass | ResourceTranscoder |
---|---|---|---|
BitmapTypeRequest | Bitmap | Bitmap | UnitTranscoder |
GifTypeRequest | GifDrawable | GifDrawable | UnitTranscoder |
DrawableTypeRequest | GifBitmapWrapper | GlideDrawable | |
GenericTranscodeRequest | / | / | UnitTranscoder |
小结:
- ResourceTranscoder 的注册与 DataLoadProvider 类似,只是使用的参数类型不同,ResourceTranscoder 是通过
decodedClass, transcodedClass
参数类型作为 Key 来进行映射的 。- 在不同类型的 Request 构建过程中,传入对应的
decodedClass, transcodedClass
类型进行匹配。- ResourceTranscoder 的作用与 Transform 类似,都是对数据进行操作,只是 ResourceTranscoder 可以改变返回的数据类型,而Transform 不能改变返回的数据类型。
- 如果
decodedClass, transcodedClass
类型一致,即表示转换前后类型没有发生改变,就使用UnitTranscoder进行透传。
七、总结
- 了解 LoadProvider 的作用,及两个子类的差异。
- 了解 DataFetcher 的作用,及其返回值类型的种类 (两种)。
- 了解 ModelLoader 泛型参数对 DataFetcher 的影响,及其责任链调用流程。
- 了解 ModelLoader 注册表的数据结构及其存/取流程。
- 了解 ModelLoader 与 DataFetcher 的关系。
- 了解 ResourceTranscoder 的作用,即数据类型转换,与 Transform 类似 。
- 了解不同类型 Request 对应的 ResourceTranscoder 类型。