Glide 源码分析
前言
Glide 是一个快速、高效的 Android 图片加载库,它提供了易用的 API,高性能、可扩展的图片解码管道以及自动的资源池技术。
Glide 的缓存术语:
- Active 内存缓存:正在使用的图片对应的内存缓存
- Cache 内存缓存:不在使用的图片对应的内存缓存
- Data 磁盘缓存:原始数据对应的磁盘缓存
- Resource 磁盘缓存:解码后数据对应的磁盘缓存
文档目录
基本使用
1 | Glide.with(context).load(url).into(view) |
Glide 初始化
初始化

Glide 使用单例模式进行管理,所以第一次调用 Glide.with(context) 时,会触发 Glide 的初始化。
Glide 初始化的工作主要是:
GlideContext,Engine,Registry等对象的创建- 向
Registry中注册各种工具类:ModelLoaderRegistry:注册的数据类型是ModelLoaderFactory,可以用于构建ModelLoader对象,用于加载图片数据EncodeRegistry:注册的数据类型是Encoder,用于对原始图片数据编码保存ResourceDecoderRegistry:注册的数据类型是ResourceDecoder,用于解码图片数据ResourceEncoderRegistry:注册的数据类型是ResourceEncoder,用于对解码后的数据编码保存ResourceTranscoderRegistry:注册的数据类型是ResourceTranscoder,用于对解码后的Resource数据进行转码
请求管理

RequestManager的职责:用于管理请求和启动请求,但是它并不直接管理Request,而是交由RequestTracker管理Request的启动,取消,暂停等Request的职责:为Target加载Resource,它定义了请求的开始、结束、状态获取等。
生命周期

Glide 通过 Glide.with(context) 中传入的 context 管理生命周期,主要原理是在 Context 对应的 Activity 中添加一个透明的 Fragment - SupportRequestManagerFragment
Activity生命周期变化 ->SupportRequestManagerFragment生命周期变化 ->ActivityFragmentLifecycle相应的方法 -> 遍历LifecycleListener集合,回调对应的方法Glide.with中会创建RequestManager对象,RequestManager实现了LifecycleListener接口,在其构造方法中会添加到ActivityFragmentLifecycle持有的LifecycleListener集合中
生命周期无效的场景:
- 当 Glide.with 传入的是
Application时,是没有生命周期管理的 - 当 Glide.with 调用线程是子线程时,也是没有生命周期管理的
- 当 Glide.with 传入的是
View时,会先找到其所在的Activity,如果找不到就会使用Application作为参数,就会转变成场景 1
View 如果没有 attach,用 Glide 加载图片会怎么样?
- 如果请求的时候通过
override设置了图片大小,那么即使 View 还没有 attach,此时依然会触发 Glide 请求图片的流程- 如果请求的时候没有设置图片大小,那么 Glide 会自己计算 View 的大小。当计算出合法的大小后,才会触发 Glide 请求图片的流程
关于 Glide 的初始化过程,代码分析部分可以参考:
Glide 加载

Glide 加载网络图片的步骤:
- 数据加载:根据 load 步骤传入的 model,使用
ModelLoader接口加载对应的图片数据到内存中。如果磁盘缓存策略允许缓存原始数据,这里会使用Encoder接口缓存原始数据到 Data 磁盘缓存。 - 解码:使用
ResourceDecoder对图片数据进行解码,得到一个持有解码结果的Resource数据。同时,这里可能会有变换和转码步骤。- 变换(
Transformation):可以获取资源并修改,通常变换操作是用来完成剪裁或对位图应用过滤器,但它也可以用于转换 GIF 动画,甚至自定义的资源类型。Glide 内置了CenterCrop、FitCenter、CircleCenter - 转码:使用
ResourceTranscoder接口将一个Resource类型转换为另一个Resource类型,例如将BitmapResource转化为LazyBitmapDrawableResource
- 变换(
- 显示:在主线程回调,先将结果保存到 Active 内存缓存中,再添加到对应的
Target显示。同时如果磁盘缓存策略允许缓存解码后的图片,这里会使用ResourceEncoder接口缓存解码后的数据到 Resource 磁盘缓存。
数据加载
Engine

Engine:用于管理 Active 内存缓存、Cache 内存缓存和图片加载的启动EngineJob:用于管理DecodeJob运行的线程、添加/移除DecodeJob执行结果的回调、通知DecodeJob执行结果的回调DecodeJob:解码磁盘缓存数据或者原始数据、执行变换和转码DataFetcherGenerator有 3 个实现者,如下:ResourceCacheGenerator:加载磁盘中解码后的图片数据DataCacheGenerator:加载磁盘中原始图片数据SourceGenerator:加载网络图片数据

- Engine#load 首先会去 ActiveResource 和 MemoryCache 两个内存缓存中加载数据
- 如果内存缓存加载失败,就会创建一个
EngineJob,在特定线程运行DecodeJob,用于获取磁盘/网络数据。DecodeJob会在子线程中依次使用ResourceCacheGenerator,DataCacheGenerator,SourceGenerator加载图片数据
关于 Engine#load 过程,详情可以参考:Engine::load 源码分析
LoadData

DataFetcherGenerator会根据 load 过程中设置的 model 从Registry中找到满足条件的ModelLoader来构建LoadData对象。ModelLoader->LoadData->DataFetcher用于加载数据,并通过DataCallback接口回调结果。- 以
SourceGenerator为例,正常情况下会通过HttpUrlFetcher加载网络数据,然后通过DataCallback接口回调给SourceGenerator,SourceGenerator会通过DataFetcherGenerator.FetcherReadyCallback接口回调给DecodeJob。- 如果需要磁盘缓存,调用
SourceGenerator#cacheData将原始数据写入到 Data 磁盘缓存 - 如果不需要磁盘缓存,直接解码展示
- 如果需要磁盘缓存,调用
关于 LoadData 的获取过程,详情可以参考:DecodeHelper::getLoadData 源码分析
关于 SourceGenerator 加载数据的过程,详情可以参考:SourceGenerator::startNextLoad 源码分析
解码
LoadPath

根据数据加载过程得到的数据的 dataClass、RequestOptions 中的 resourceClass, load 过程中设置的 model 的 transcodeClass 为参数,从 ResourceDecoderRegistry 和 ResourceTranscoderRegistry 中找到符合条件的 ResourceDecoder 和 ResourceTranscoder,构建 LoadPath 对象用于解码。
关于 LoadPath 的获取,详情可以参考:DecodeHelper::getLoadPath 源码分析

解码过程:
- 调用符合条件的
ResourceDecoder#decode方法解码,最终会调用到Downsampler#decode方法。Downsampler类的职责是:根据图像的 exif 方向,使用BitmapFactory对图像进行下采样、解码和旋转
- 通过
DecodeCallback#onResourceDecoded接口回调给DecodeJob,用于进行Transformation变换操作 - 调用符合条件的
ResourceTranscoder#transcode方法转码
关于解码过程,详情可以参考:DecodeJob::decodeFromData 源码分析
关于解码过程中的优化,详情可以参考:Glide - 内存优化
显示

DecodeJob 完成图片数据加载、解码等工作后:
- 先通过
Decode.Callback接口回调给EngineJobEngineJob通过EngineJobListener#onEngineJobComplete接口回调Engine,Engine收到回调后,就会将图片数据放入 Active 内存缓存中。EngineJob通过ResourceCallback接口回调给SingleRequest。SingleRequest收到回调后,就会将图片设置到Target中显示。
- 继续在当前子线程内,调用
DeferredEncodeManager#encode方法进行编码,将解码后的图片数据写入到 Resource 磁盘缓存
Glide 缓存
内存缓存

Active 内存缓存
ActiveResources 是 Active 内存缓存的实现类,它是用来存储当前界面正在使用的图片。原理是 HashMap + 弱引用机制。
- 读取:
Engine#load第一步就是从ActiveResources 中读取数据 - 写入:
DecodeJob加载、解码等完成后,在显示到Target 之前,会写入到 Active 内存缓存中Engine#load第二步,从 Cache 内存缓存中读取成功时,会写入到 Active 内存缓存中
- 删除:
EngineResource持有的 acquired 引用计数为 0 的时候- 内存不足,进行 GC 的时候
Active 内存缓存中的数据被 GC 回收了,如何及时写入到 LruResourceCache 中呢?
ActiveResource 启动了一个线程专门去扫描 ReferenceQueue,只要调用 ReferenceQueue 的 remove 方法能取到元素,就说明 EngineResource 被回收了,可以移动到 LruCache 里面了
GC 的时候为啥可以将持有的图片放入 Cache 内存缓存中呢?
通过在 WeakReference 中用一个强引用对象保存资源实现。可以参考下面的 demo,TestWeakReference 保存了 EngineResource 中的 Resource。
Cache 内存缓存
LruResourceCache 是 Cache 内存缓存的实现类,它是用来存储当前没有使用到的图片。原理是 LruCache,使用 LinkedHashMap 以强引用的方式存储图片数据,缓存满时会移除较早缓存的图片。
- 读取:Active 内存缓存没有命中时,就会从 Cache 内存缓存中读取数据
- 写入:Active 内存缓存数据被删的时候,就会写入到
LruResourceCache中 - 删除:LRU 算法规则
为什么要设置两种内存缓存?
为了保护这些正在使用的图片不会被 LruCache 算法回收掉。
磁盘缓存
不管是 Resource 磁盘缓存还是 Data 磁盘缓存,都是使用 DiskLruCache 类实现,只不过两者存储的 Key 不同而已。
DiskLruCache 是一种使用有限数量的缓存空间来缓存文件的硬盘缓存,采用了 LRU 算法在一定的空间大小下来缓存经常使用到的文件
关于 DiskLruCache 的代码分析:DiskLruCache 源码分析
Resource 磁盘缓存
- 读取:两个内存缓存都没有命中时,就会读取
- 写入:图片解码并显示到
Target后,会将其写入 Resource 磁盘缓存,使用的 key 是ResourceCacheKey - 删除:LRU 算法规则
Data 磁盘缓存
- 读取:两个内存缓存和 Resource 磁盘缓存都没有命中时,就会读取
- 写入:原始图片加载完成后,会将其写入 Data 磁盘缓存,使用的 key 是
DataCacheKey - 删除:LRU 算法规则
关于 Glide 缓存,代码分析部分参考:Glide - 三级缓存