前言

Glide 是一个快速、高效的 Android 图片加载库,它提供了易用的 API,高性能、可扩展的图片解码管道以及自动的资源池技术。

Glide 的缓存术语:

  1. Active 内存缓存:正在使用的图片对应的内存缓存
  2. Cache 内存缓存:不在使用的图片对应的内存缓存
  3. Data 磁盘缓存:原始数据对应的磁盘缓存
  4. Resource 磁盘缓存:解码后数据对应的磁盘缓存

文档目录

基本使用

1
Glide.with(context).load(url).into(view)

Glide 初始化

初始化

Glide 使用单例模式进行管理,所以第一次调用 Glide.with(context) 时,会触发 Glide 的初始化。

Glide 初始化的工作主要是:

  1. GlideContextEngineRegistry 等对象的创建
  2. Registry 中注册各种工具类:
    1. ModelLoaderRegistry:注册的数据类型是 ModelLoaderFactory,可以用于构建 ModelLoader 对象,用于加载图片数据
    2. EncodeRegistry:注册的数据类型是 Encoder,用于对原始图片数据编码保存
    3. ResourceDecoderRegistry:注册的数据类型是 ResourceDecoder,用于解码图片数据
    4. ResourceEncoderRegistry:注册的数据类型是 ResourceEncoder,用于对解码后的数据编码保存
    5. ResourceTranscoderRegistry:注册的数据类型是 ResourceTranscoder,用于对解码后的 Resource 数据进行转码

请求管理

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

生命周期

Glide 通过 Glide.with(context) 中传入的 context 管理生命周期,主要原理是在 Context 对应的 Activity 中添加一个透明的 Fragment - SupportRequestManagerFragment

  1. Activity 生命周期变化 -> SupportRequestManagerFragment 生命周期变化 -> ActivityFragmentLifecycle 相应的方法 -> 遍历 LifecycleListener 集合,回调对应的方法
  2. Glide.with 中会创建 RequestManager 对象,RequestManager 实现了 LifecycleListener 接口,在其构造方法中会添加到 ActivityFragmentLifecycle 持有的 LifecycleListener 集合中

生命周期无效的场景:

  1. 当 Glide.with 传入的是 Application 时,是没有生命周期管理的
  2. 当 Glide.with 调用线程是子线程时,也是没有生命周期管理的
  3. 当 Glide.with 传入的是 View 时,会先找到其所在的 Activity,如果找不到就会使用 Application 作为参数,就会转变成场景 1

View 如果没有 attach,用 Glide 加载图片会怎么样?

  1. 如果请求的时候通过 override 设置了图片大小,那么即使 View 还没有 attach,此时依然会触发 Glide 请求图片的流程
  2. 如果请求的时候没有设置图片大小,那么 Glide 会自己计算 View 的大小。当计算出合法的大小后,才会触发 Glide 请求图片的流程

关于 Glide 的初始化过程,代码分析部分可以参考:

  1. Glide::with 源码分析
  2. RequestManager::load 源码分析

Glide 加载

Glide 加载网络图片的步骤:

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

数据加载

Engine

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

  1. Engine#load 首先会去 ActiveResource 和 MemoryCache 两个内存缓存中加载数据
  2. 如果内存缓存加载失败,就会创建一个 EngineJob ,在特定线程运行 DecodeJob,用于获取磁盘/网络数据。DecodeJob 会在子线程中依次使用 ResourceCacheGeneratorDataCacheGeneratorSourceGenerator 加载图片数据

关于 Engine#load 过程,详情可以参考:Engine::load 源码分析

LoadData

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

关于 LoadData 的获取过程,详情可以参考:DecodeHelper::getLoadData 源码分析
关于 SourceGenerator 加载数据的过程,详情可以参考:SourceGenerator::startNextLoad 源码分析

解码

LoadPath

根据数据加载过程得到的数据的 dataClassRequestOptions 中的 resourceClass, load 过程中设置的 model 的 transcodeClass 为参数,从 ResourceDecoderRegistryResourceTranscoderRegistry 中找到符合条件的 ResourceDecoderResourceTranscoder,构建 LoadPath 对象用于解码。

关于 LoadPath 的获取,详情可以参考:DecodeHelper::getLoadPath 源码分析

解码过程:

  1. 调用符合条件的 ResourceDecoder#decode 方法解码,最终会调用到 Downsampler#decode 方法。
    • Downsampler 类的职责是:根据图像的 exif 方向,使用 BitmapFactory 对图像进行下采样、解码和旋转
  2. 通过 DecodeCallback#onResourceDecoded 接口回调给 DecodeJob,用于进行 Transformation 变换操作
  3. 调用符合条件的 ResourceTranscoder#transcode 方法转码

关于解码过程,详情可以参考:DecodeJob::decodeFromData 源码分析
关于解码过程中的优化,详情可以参考:Glide - 内存优化

显示

DecodeJob 完成图片数据加载、解码等工作后:

  1. 先通过 Decode.Callback 接口回调给 EngineJob
    1. EngineJob 通过 EngineJobListener#onEngineJobComplete 接口回调 EngineEngine 收到回调后,就会将图片数据放入 Active 内存缓存中。
    2. EngineJob 通过 ResourceCallback 接口回调给 SingleRequestSingleRequest 收到回调后,就会将图片设置到 Target 中显示。
  2. 继续在当前子线程内,调用 DeferredEncodeManager#encode 方法进行编码,将解码后的图片数据写入到 Resource 磁盘缓存

Glide 缓存

内存缓存

Active 内存缓存

ActiveResources 是 Active 内存缓存的实现类,它是用来存储当前界面正在使用的图片。原理是 HashMap + 弱引用机制。

  1. 读取:Engine#load 第一步就是从 ActiveResources 中读取数据
  2. 写入:
    1. DecodeJob 加载、解码等完成后,在显示到 Target 之前,会写入到 Active 内存缓存中
    2. Engine#load 第二步,从 Cache 内存缓存中读取成功时,会写入到 Active 内存缓存中
  3. 删除:
    1. EngineResource 持有的 acquired 引用计数为 0 的时候
    2. 内存不足,进行 GC 的时候

Active 内存缓存中的数据被 GC 回收了,如何及时写入到 LruResourceCache 中呢?
ActiveResource 启动了一个线程专门去扫描 ReferenceQueue,只要调用 ReferenceQueue 的 remove 方法能取到元素,就说明 EngineResource 被回收了,可以移动到 LruCache 里面了

GC 的时候为啥可以将持有的图片放入 Cache 内存缓存中呢?
通过在 WeakReference 中用一个强引用对象保存资源实现。可以参考下面的 demo,TestWeakReference 保存了 EngineResource 中的 Resource。

Cache 内存缓存

LruResourceCache 是 Cache 内存缓存的实现类,它是用来存储当前没有使用到的图片。原理是 LruCache,使用 LinkedHashMap 以强引用的方式存储图片数据,缓存满时会移除较早缓存的图片。

  1. 读取:Active 内存缓存没有命中时,就会从 Cache 内存缓存中读取数据
  2. 写入:Active 内存缓存数据被删的时候,就会写入到 LruResourceCache
  3. 删除:LRU 算法规则

为什么要设置两种内存缓存?
为了保护这些正在使用的图片不会被 LruCache 算法回收掉。

磁盘缓存

不管是 Resource 磁盘缓存还是 Data 磁盘缓存,都是使用 DiskLruCache 类实现,只不过两者存储的 Key 不同而已。
DiskLruCache 是一种使用有限数量的缓存空间来缓存文件的硬盘缓存,采用了 LRU 算法在一定的空间大小下来缓存经常使用到的文件

关于 DiskLruCache 的代码分析:DiskLruCache 源码分析

Resource 磁盘缓存

  1. 读取:两个内存缓存都没有命中时,就会读取
  2. 写入:图片解码并显示到 Target 后,会将其写入 Resource 磁盘缓存,使用的 key 是 ResourceCacheKey
  3. 删除:LRU 算法规则

Data 磁盘缓存

  1. 读取:两个内存缓存和 Resource 磁盘缓存都没有命中时,就会读取
  2. 写入:原始图片加载完成后,会将其写入 Data 磁盘缓存,使用的 key 是 DataCacheKey
  3. 删除:LRU 算法规则

关于 Glide 缓存,代码分析部分参考:Glide - 三级缓存