Glide 框架结构浅析
一、概述
1.1 背景
Glide 是一个图片加载库,跟它同类型的库还有 Picasso、Fresco、Universal-Image-Loader 等。基于 Glide 优秀的缓存管理策略和生命周期关联的特点,目前市面上对 Glide 的使用非常广,因此我们有必要深入研究下 Glide 相关的实现原理,便于更好的使用它。
目前 Glide 版本已经更新到 4.11.0,但本系列我们仍采用 Glide3.7.0 版本进行分析,原因有两个:
- 3.7.0 版本代码相对比较简单。
- 与 3.7.0 版本相比,4.0+ 版本之后代码改动较大。
所以本着先易后难的原则,我们使用 Glide 3.7.0 来进行分析,对 Glide 整体有一个全面了解之后,再去分析 4.0+ 版本。
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 支持 Gif、WebP、Jpeg、Png 等格式的图片。
- 生命周期的绑定:图片请求与页面生命周期绑定,避免内存泄漏。
- 使用简单(链式调用),且提供丰富的 Api 功能 (如: 图片裁剪等功能)。
- 高效的缓存策略:
1. 支持多种缓存策略 (Memory 和 Disk 图片缓存)。
2. 根据 ImageView 的大小来加载相应大小的图片尺寸。
3. 内存开销小,默认使用 RGB_565 格式 (3.x 版本)。
4. 复用对象 (享元模式),降低内存的抖动。
5. 通过 Lru 算法来管理内存缓存和磁盘缓存,保证资源使用的可控性。
1.3 系列文章
- Glide系列(一) — Glide 框架结构浅析
- Glide系列(二) — DataLoadProvider 及与 Encoder&Decoder 的关系
- Glide系列(三) — LoadProvider、ModelLoader、DataFetcher 和 ResourceTranscoder 关系
- Glide系列(四) — Glide 缓存流程分析
- Glide系列(五) — Request 构建流程分析
- DiskLruCache 源码分析
二、Glide 框架整体结构设计
Glide 框架整体结构如下图所示。
Glide 框架结构如上图所示:
- Glide 框架主要分为两大流程:
1.1图片请求的构建流程
。
1.2图片缓存的获取流程
。- 图片请求构建流程内又分为四个模块:
2.1用户态的请求模块:
用户使用 Glide 进行链式调用的时候会生成一个用户态的 Request。
2.2真实的请求模块:
由于步骤2.1中会根据场景构建多种Request,因此在发起图片请求时需要进行收口,因此会使用用户态的 Request 构建一个真正的 GenericRequest 请求。
2.3Request 生命周期管理模块:
Glide 的一大特点是会将图片的请求和页面生命周期进行绑定,避免出现内存泄漏的风险,因此会有一个 Request 生命周期管理模块。
2.4Registry中心模块:
由于 Glide 支持加载多种类型的数据,因此在注册中心会预先注册所支持类型处理类的信息。- 图片缓存一共分为3大层,5小层:
3.1内存缓存(2小层):
弱引用缓存、LruCache。
3.2本地缓存(2小层):
本地 ResultCache 缓存、本地 SourceCache 缓存。
3.3Source 数据源(1小层):
网络获取、本地 AssetPath 获取、其它本地图片。
小结:
看到这里,只要知道以下三点就可以了。
- 知道 Glide 分为两大流程 (请求创建流程、缓存获取流程) 就可以了。
- 知道 Glide 缓存部分分为3大层,5小层就可以了。
- 可以将 Glide 的两大流程与网络请求进行类比。网络请求也分为两个流程:网络请求的构建流程、数据的接收和响应流程 (Glide的缓存获取流程)。
- 这里我们对五层缓存进行定义,方便后面引用说明:
- 第一层:内存缓存 -> 弱应用缓存(ActiveResource)。
- 第二层:内存缓存 -> LruCache
- 第三层:磁盘缓存 -> ResultCache (经过transform的图)
- 第四层:磁盘缓存 -> SourceCache (从DataFetcher 直接拉取到的数据)
- 第五层:数据源 -> Source
三、Glide中涉及的几个基本概念
在研究 Glide 之前,需要对下面几个类有一个基本概念。
Glide中涉及的几个基本概念:
Target、ViewTarget
是什么?Resource
是什么?Encoder、ResourceEncoder
是什么?ResourceDecoder
是什么?Transformation
是什么?ResourceTranscoder
是什么?DataFetcher、ModelLoader
是什么?DataLoadProvider、LoadProvider
是什么?DataLoadProvider、ModelLoader、DataFetcher
之间的关系?
3.1 Target、ViewTarget 是什么?
- Target、ViewTarget 的作用类似于网络请求中的 Callback,主要是用于处理图片已经返回后的逻辑。主要的回调方法如下图所示。
- Target 与 ViewTarget 的主要区别在于 ViewTarget 中包含有一个设置图片的 View。
- Target 实现了 LifecycleListener,方便在处理生命周期时调用对应的方法。
public interface Target<R> extends LifecycleListener {
// 开始请求
void onLoadStarted(Drawable placeholder);
// 请求失败
void onLoadFailed(Exception e, Drawable errorDrawable);
// 请求成功
void onResourceReady(R resource, GlideAnimation<? super R> glideAnimation);
}
3.2 Resource 是什么?
- Resource 的作用类似于网络请求返回时统一的数据封装 Result,目的是便于数据的传输。
- 同时针对 Resource 包含的不同数据类型,获取相应的编解码器等。
// 网络接口数据封装
class Result<T> {
T data;
}
// 用于包装从缓存中获取的数据(图片)
public interface Resource<Z> {
Z get();
}
// 具体的使用场景
public class BitmapResource implements Resource<Bitmap> {
private final Bitmap bitmap;
@Override
public Bitmap get() {
return bitmap;
}
}
3.3 Encoder、ResourceEncoder 是什么?
Encoder、ResourceEncoder
用于执行将图片信息保存成文件进行缓存。encode()
方法会传入要保存的数据和OutputStream 两个参数,所以 Encoder 主要是执行。Encoder.encode()
方法接收任意类型的输入,且将数据写入到 OutputStream 中。ResourceEncoder.encode()
方法接收 Resource 类型的数据。
public interface Encoder<T> {
// 接收任意类型的输入,并将数据写入到 OutputStream 中。
boolean encode(T data, OutputStream os);
}
public interface ResourceEncoder<T> extends Encoder<Resource<T>> {
// 接收Resource<T>类型的数据,并将数据写入到 OutputStream 中。
boolean encode(Resource<T> data, OutputStream os);
}
// 具体的实现类:将Bitmap保存成文件。
public class BitmapEncoder implements ResourceEncoder<Bitmap> {
private static final int DEFAULT_COMPRESSION_QUALITY = 90;
private Bitmap.CompressFormat compressFormat;
private int quality;
@Override
public boolean encode(Resource<Bitmap> resource, OutputStream os) {
final Bitmap bitmap = resource.get();
Bitmap.CompressFormat format = getFormat(bitmap);
// 压缩
bitmap.compress(format, quality, os);
return true;
}
}
3.4 ResourceDecoder 是什么?
ResourceDecoder
与ResourceEncoder
类似,主要用于将图片缓存(文件)解析成要使用的数据格式(如Bitmap)。ResourceDecoder.decode()
方法接收任意类型(只要能拿到缓存数据即可:如InputStream、File等),返回的类型需要使用Resource包装,方便传输。
public interface ResourceDecoder<T, Z> {
// 接收任意类型(只要能拿到缓存数据即可:如InputStream、File等),返回的类型需要使用Resource包装,方便传输。
Resource<Z> decode(T source, int width, int height) throws IOException;
}
public class StreamBitmapDecoder implements ResourceDecoder<InputStream, Bitmap> {
private final Downsampler downsampler;
// 具体场景:接收File文件,然后转换成Bitmap格式后进行包装,最终返回Resource<Bitmap>数据类型。
@Override
public Resource<Bitmap> decode(InputStream source, int width, int height) {
Bitmap bitmap = downsampler.decode(source, bitmapPool, width, height, decodeFormat);
return BitmapResource.obtain(bitmap, bitmapPool);
}
}
3.5 Transformation 是什么?
Transformation
是一个数据转换器,转换前后数据的类型不变。它的主要作用是对返回的数据进行一些加工处理,如倒圆角等,它与ResourceTranscoder
的作用是不同的。
public interface Transformation<T> {
// 转换前后数据的类型不变。
Resource<T> transform(Resource<T> resource, int outWidth, int outHeight);
}
3.6 ResourceTranscoder 是什么?
ResourceTranscoder
是一个转码器,会改变数据类型 (将 Resource 的参数类型转换为 Resource 类型并返回)。与 Transformation 不同。- 所以
Transformation
和ResourceTranscoder
就组成了数据同类型转换
和不同类型转换
的功能。
public interface ResourceTranscoder<Z, R> {
// 将 Resource<Z> 的参数类型转换为 Resource<R> 类型。
Resource<R> transcode(Resource<Z> toTranscode);
}
// 具体示例:将Bitmap转换为GlideBitmapDrawable类型。
public class GlideBitmapDrawableTranscoder implements ResourceTranscoder<Bitmap, GlideBitmapDrawable> {
@Override
public Resource<GlideBitmapDrawable> transcode(Resource<Bitmap> toTranscode) {
GlideBitmapDrawable drawable = new GlideBitmapDrawable(resources, toTranscode.get());
return new GlideBitmapDrawableResource(drawable, bitmapPool);
}
}
3.7 DataFetcher、ModelLoader 是什么?
DataFetcher
是具体加载数据的类 (如: 从网络上加载图片数据)。ModelLoader
的功能主要是预处理传入DataFetcher
的参数,以及返回对应的DataFetcher
的类型 (代码如下所示)。
public interface DataFetcher<T> {
// 加载数据
T loadData(Priority priority) throws Exception;
}
// 具体示例:
public class HttpUrlFetcher implements DataFetcher<InputStream> {
@Override
public InputStream loadData(Priority priority) throws Exception {
return loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
}
}
public interface ModelLoader<T, Y> {
DataFetcher<Y> getResourceFetcher(T model, int width, int height);
}
public abstract class UriLoader<T> implements ModelLoader<Uri, T> {
private final ModelLoader<GlideUrl, T> urlLoader;
@Override
public final DataFetcher<T> getResourceFetcher(Uri model, int width, int height) {
final String scheme = model.getScheme();
DataFetcher<T> result = null;
// 加载本地路径数据的DataFetcher。
if (isLocalUri(scheme)) {
result = getLocalUriFetcher(context, model);
} else if (urlLoader != null && ("http".equals(scheme) || "https".equals(scheme))) {
// 加载非本地路径数据的DataFetcher。
result = urlLoader.getResourceFetcher(new GlideUrl(model.toString()), width, height);
}
return result;
}
protected abstract DataFetcher<T> getLocalUriFetcher(Context context, Uri uri);
}
3.8 DataLoadProvider、LoadProvider 是什么?
DataLoadProvider
是一个数据的提供者,主要是负责对数据进行处理 (如将Bitmap保存为缓存文件,将缓存文件转换为Bitmap),因此DataLoadProvider
主要是对外提供编解码的对象 (Encoder、Decoder
)。LoadProvider
继承自DataLoadProvider
,所以它具备将文件的编解码功能,加载文件的能力,对数据进行类型转换的能力。DataLoadProvider
中两个泛型参数<T, Z>的含义如下面代码所示 (需要理解:看下面代码
)。- 要将一个图片缓存文件展示到屏幕上的步骤:
- 通过
LoadProvider
获取到ModelLoader
,继而找到加载文件的DataFetcher
,此时返回的数据要么是流,要么是fd。- 通过
LoadProvider
获取到ResourceDecoder
解码器对DataFetcher
返回的数据进行解码生成T,并返回Resource。- 通过
LoadProvider
获取到ResourceTranscoder
转码器对 Resource进行操作,返回Resource后设置给控件。
代码中的注释说明很重要。
/*
* @param <T> 对输入类型为T的数据进行解码。
* @param <Z> 解码后返回的类型为Resource<Z>。
*/
public interface DataLoadProvider<T, Z> {
// Cache缓存的解码器只支持接收File类型,返回Resource<Z>类型的数据。
// Cache缓存:指第3、4层缓存,即ResultCache、SourceCache缓存。
// 为什么Cache解码器只支持传入File进行解码? 因为这两层的缓存是Glide自己生成的,所以确定是File类型
ResourceDecoder<File, Z> getCacheDecoder();
// Source(指第5层缓存,即网络数据,asset目录数据等)的解码器支持接收T类型,返回Resource<Z>类型的数据。
ResourceDecoder<T, Z> getSourceDecoder();
// 接收T类型的数据进行编码(转成文件)。
// 将从第5层缓存(数据源)获取到的数据缓存到第4层。
Encoder<T> getSourceEncoder();
// 接收Resource<Z>类型的数据进行编码(转成文件)。
// 对经过transform()之后的数据进行编码(保存成文件)。即将第四层SourceCache数据缓存到第三层ResultCache。
ResourceEncoder<Z> getEncoder();
}
/**
* 泛型参数含义:
* A、T:将A类型的数据通过ModelLoader加载成T类型数据,如将File类型的数据加载成InputStream类型数据。
* Z、R:将Z类型的数据通过转码器转换成R类型数据,如将Bitmap类型的数据转成Drawable类型数据。
*/
public interface LoadProvider<A, T, Z, R> extends DataLoadProvider<T, Z> {
ModelLoader<A, T> getModelLoader();
ResourceTranscoder<Z, R> getTranscoder();
}
DataLoadProvider 中几种 编/解码器
的使用场景如下图所示:
3.9 DataLoadProvider、ModelLoader、DataFetcher 之间的关系?
介绍完了上面部分后,我们来看下他们之间的关系,如下图所示:
DataLoadProvider、ModelLoader、DataFetcher 之间的关系
小结: 看到这里,需要了解如下信息。
- DataLoadProvider 内持有处理图片相关的编/解码器。
- LoadProvider 是一个聚合类,继承自 DataLoadProvider 接口,同时聚合了 ModelLoader 和 ResourceTranscoder。因此具备了数据加载能力,编解码能力,数据转码能力。
- ModelLoader 会根据提供的加载类型返回一个相应的 DataFetcher。
四、总结
- 了解本文中涉及的
Resource、Encoder、ResourceDecoder、ResourceTranscoder
等的基本概念及其作用。 - 了解
DataLoadProvider、ModelLoader、DataFetcher
是什么,它们之间的关系? - 了解Glide的缓存的分层及其类型。