深入剖析 Glide 核心架构与源码机制
深入剖析 Glide 核心架构与源码机制
在掌握了 Glide 的高级用法之后,仅仅停留在 API 调用层面是不够的。作为一个工业级图片加载框架,Glide 如何在内存极其有限的移动端环境下,优雅地处理 Bitmap 这种"内存杀手"?它又是如何实现与宿主生命周期的完美契合,避免内存泄漏的?
本文将深入 Glide 源码,从一行 Glide.with(this).load(url).into(imageView) 出发,沿着调用链逐级下钻,剖析其生命周期管理机制、完整的请求执行流水线、多级缓存架构以及复杂的异步任务调度系统。
一、 全局架构俯瞰
在深入细节之前,先建立一个全局视角。Glide 的整体架构可以分为三个大的层次:
graph TB
subgraph "API 层 (调用入口)"
A["Glide.with(context)"] --> B["RequestManager"]
B --> C["RequestBuilder"]
C --> D[".into(imageView)"]
end
subgraph "请求管理层 (Request Layer)"
D --> E["SingleRequest"]
E --> F["Engine.load()"]
end
subgraph "引擎层 (Engine Layer)"
F --> G{"内存缓存命中?"}
G -- "命中 ActiveResources" --> H["直接返回资源"]
G -- "命中 MemoryCache" --> H
G -- "未命中" --> I["创建 EngineJob + DecodeJob"]
end
subgraph "数据获取层 (Data Layer)"
I --> J["DecodeJob 状态机"]
J --> K["ResourceCacheGenerator<br />(磁盘-变换后缓存)"]
K -- "未命中" --> L["DataCacheGenerator<br />(磁盘-原始数据缓存)"]
L -- "未命中" --> M["SourceGenerator<br />(网络/本地源)"]
end
subgraph "编解码层 (Codec Layer)"
M --> N["ModelLoader → DataFetcher<br />(数据抓取)"]
N --> O["ResourceDecoder<br />(数据解码为 Bitmap)"]
O --> P["Transformation<br />(变换处理)"]
P --> Q["ResourceEncoder<br />(写入磁盘缓存)"]
end
Q --> H
二、 一切的起点:Glide.with() 与生命周期绑定
我们在使用 Glide 时,第一步永远是 Glide.with(context)。这一行代码看似平淡,背后却完成了两件极其关键的事情:获取全局 Glide 单例和绑定生命周期。
2.1 Glide 单例的初始化
Glide.with() 内部首先会调用 Glide.get(context) 来获取(或初始化)全局单例。这个单例的初始化过程非常关键:
// Glide.java 源码片段
private static volatile Glide glide;
public static Glide get(@NonNull Context context) {
if (glide == null) {
// 使用 DCL(双重校验锁)确保线程安全的单例初始化
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context, annotationGeneratedModule);
}
}
}
return glide;
}
在 checkAndInitializeGlide 中,Glide 会通过反射加载 @GlideModule 注解处理器在编译时期生成的 GeneratedAppGlideModuleImpl 类(如果有的话),从中获取开发者自定义的 applyOptions 和 registerComponents 配置。随后,GlideBuilder.build() 会创建全局的核心组件:
// GlideBuilder.java 源码片段 (build 方法)
Glide build(@NonNull Context context) {
// 1. 创建网络请求线程池
if (sourceExecutor == null) {
sourceExecutor = GlideExecutor.newSourceExecutor();
}
// 2. 创建磁盘缓存读取线程池
if (diskCacheExecutor == null) {
diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
}
// 3. 创建动画线程池
if (animationExecutor == null) {
animationExecutor = GlideExecutor.newAnimationExecutor();
}
// 4. 创建内存大小计算器
if (memorySizeCalculator == null) {
memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
}
// 5. 创建 Bitmap 复用池
if (bitmapPool == null) {
int size = memorySizeCalculator.getBitmapPoolSize();
bitmapPool = new LruBitmapPool(size);
}
// 6. 创建内存缓存 (LruCache)
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
// 7. 创建磁盘缓存工厂
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
// 8. 创建核心引擎 Engine
Engine engine = new Engine(
memoryCache, diskCacheFactory,
diskCacheExecutor, sourceExecutor,
GlideExecutor.newUnlimitedSourceExecutor(),
animationExecutor, isActiveResourceRetentionAllowed);
// 9. 组装最终的 Glide 实例
return new Glide(context, engine, memoryCache, bitmapPool, ...);
}
关键洞察: Glide 在初始化阶段就完成了线程池创建、内存计算和缓存分配。
MemorySizeCalculator会根据设备的屏幕密度、宽高和内存等级,动态计算出BitmapPool和MemoryCache应该占据多少内存。这种自适应策略使得 Glide 在低端机和高端机上都能高效运行。
2.2 RequestManagerRetriever:隐藏 Fragment 注入机制
获得 Glide 单例后,with() 的第二步是通过 RequestManagerRetriever 获取一个与当前 UI 组件生命周期绑定的 RequestManager。
Glide 是如何感知 Activity 或 Fragment 的生命周期的?答案是:无 UI 的空 Fragment (View-less Fragment)。
// RequestManagerRetriever.java 源码片段(简化逻辑)
public RequestManager get(@NonNull FragmentActivity activity) {
// 关键判断:如果在后台线程调用,则退化为 Application 级别的请求(永不自动取消)
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
}
assertNotDestroyed(activity);
FragmentManager fm = activity.getSupportFragmentManager();
// 核心:向 Activity 的 FragmentManager 注入一个不可见的 Fragment
return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
private RequestManager supportFragmentGet(
@NonNull Context context, @NonNull FragmentManager fm,
@Nullable Fragment parentHint, boolean isParentVisible) {
// 1. 尝试查找已注入的 SupportRequestManagerFragment
SupportRequestManagerFragment current =
getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
// 2. 从 Fragment 中获取绑定的 RequestManager
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// 3. 如果是首次,创建全新的 RequestManager 并绑定
Glide glide = Glide.get(context);
requestManager = factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
为什么要注入 Fragment 而不是用 LifecycleObserver? 这是一个历史原因——Glide 诞生时(2014 年前后),Android 还没有 Lifecycle 组件。用隐藏 Fragment 来感知生命周期,是当时唯一可行的非侵入式方案。不过 Glide 新版本已逐步支持 LifecycleOwner。
2.3 生命周期事件的完整分发链路
这个隐藏 Fragment 是如何将生命周期事件传递给 RequestManager 的?让我们用一张图来完整展现这条链路:
sequenceDiagram
participant Activity
participant Fragment as SupportRequestManagerFragment<br />(隐藏的空白 Fragment)
participant Lifecycle as ActivityFragmentLifecycle
participant RM as RequestManager
participant Tracker as RequestTracker
Note over Activity, Tracker: --- 页面可见,图片开始加载 ---
Activity->>Fragment: onStart()
Fragment->>Lifecycle: onStart()
Lifecycle->>RM: onStart()
RM->>Tracker: resumeRequests()
Note right of Tracker: 遍历所有 Request,<br />调用 request.begin()
Note over Activity, Tracker: --- 页面不可见,暂停加载 ---
Activity->>Fragment: onStop()
Fragment->>Lifecycle: onStop()
Lifecycle->>RM: onStop()
RM->>Tracker: pauseRequests()
Note right of Tracker: 遍历所有 Request,<br />调用 request.pause()
Note over Activity, Tracker: --- 页面销毁,清理一切 ---
Activity->>Fragment: onDestroy()
Fragment->>Lifecycle: onDestroy()
Lifecycle->>RM: onDestroy()
RM->>Tracker: clearRequests()
Note right of Tracker: 遍历所有 Request,<br />调用 request.clear(),<br />取消网络请求,释放资源
RequestTracker 内部维护了一个 Set<Request>,记录了当前页面所有正在执行的图片加载请求。当宿主触发 onDestroy 时,clearRequests() 会遍历并取消所有尚未完成的网络请求,并释放所持有的 Bitmap 资源。
通过这种"寄生"模式,Glide 实现了生命周期管理的完全解耦——开发者无需在 BaseActivity.onDestroy() 中手动去释放图片资源,彻底切断了因为异步网络请求导致的内存泄漏根源。
三、 从 into() 到 Engine.load():请求的完整执行链路
当调用 into(imageView) 时,才真正拉开了图片加载的序幕。让我们一层一层地剥开这段调用链。
3.1 RequestBuilder.into():构建 SingleRequest
// RequestBuilder.java 源码片段
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
// 1. 根据 ImageView 的 ScaleType 自动决定 Transformation
// 例如 FIT_CENTER -> FitCenter, CENTER_CROP -> CenterCrop
BaseRequestOptions<?> requestOptions = ...;
// 2. 将 ImageView 包装为一个 ViewTarget (适配器模式)
// ViewTarget 知道如何将最终的 Drawable 设置到 ImageView 上
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
Executors.mainThreadExecutor()); // 回调切回主线程的 Executor
}
紧接着在内部,RequestBuilder 会创建一个 SingleRequest 对象:
// RequestBuilder.java (简化)
private <Y extends Target<TranscodeType>> Y into(Y target, ...) {
// 1. 构建 Request 对象
Request request = buildRequest(target, targetListener, options, callbackExecutor);
// 2. 获取 target 上之前绑定的旧 Request
Request previous = target.getRequest();
// 3. 如果新旧 Request 等价且旧 Request 已经完成或正在加载,则复用
// 这是对 RecyclerView 中同一个 ViewHolder 反复绑定同一 URL 的优化
if (request.isEquivalentTo(previous) && !isSkipMemoryCacheWithCompletePreviousRequest(...)) {
request.recycle();
if (!Preconditions.checkNotNull(previous).isRunning()) {
previous.begin(); // 重新触发旧 Request
}
return target;
}
// 4. 清理旧 Request(取消网络请求,释放资源)
requestManager.clear(target);
// 5. 将新 Request 绑定到 Target 上
target.setRequest(request);
// 6. 正式提交给 RequestManager 进行跟踪和执行
requestManager.track(target, request);
return target;
}
3.2 SingleRequest.begin():状态机启动
SingleRequest 是一个严格的状态机。它内部维护了一个 Status 枚举:
// SingleRequest.java
private enum Status {
PENDING, // 等待中(刚创建)
RUNNING, // 执行中
WAITING_FOR_SIZE, // 等待 View 测量完成以确定目标尺寸
COMPLETE, // 加载完成
FAILED, // 加载失败
CLEARED, // 已被清理/取消
}
// SingleRequest.java 源码片段
public void begin() {
synchronized (requestLock) {
status = Status.WAITING_FOR_SIZE;
// 如果已经在 RequestOptions 中指定了 override(width, height)
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
// 否则需要等待 View 完成测量后才拿得到目标尺寸
// 这里使用了 ViewTreeObserver 来监听 Layout 事件
target.getSize(this);
}
// 如果在等待期间还没拿到结果,先展示 placeholder
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
}
}
为什么 Glide 如此在意 View 的尺寸? 因为 Glide 会根据目标 ImageView 的实际像素尺寸进行降采样(Downsampling)。假如一张 4000×3000 的照片只需要在一个 400×300 的 ImageView 中展示,Glide 会在解码阶段就将其缩放到 400×300,避免将一个 48MB 的 Bitmap 加载进内存(ARGB_8888 下,4000×3000×4 = 48MB)。
当 View 的尺寸确定后,调用 onSizeReady(),最终委托给 Engine.load(),这是整个框架最核心的方法。
3.3 Engine.load():缓存的查找与任务的调度
// Engine.java 源码片段
public <R> LoadStatus load(
GlideContext glideContext, Object model, Key signature,
int width, int height, Class<?> resourceClass, Class<R> transcodeClass,
Priority priority, DiskCacheStrategy diskCacheStrategy,
Map<Class<?>, Transformation<?>> transformations,
..., ResourceCallback cb) {
// 1. 构建缓存的 Key
// Key 由 model(URL)、signature、width、height、transformations 等多要素组成
// 这就是为什么同一 URL 不同尺寸的 ImageView 会产生不同的缓存
EngineKey key = keyFactory.buildKey(
model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
// ===== 第一级:查找 ActiveResources =====
EngineResource<?> active = loadFromActiveResources(key);
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
return null; // 命中,直接返回
}
// ===== 第二级:查找 MemoryCache (LruCache) =====
EngineResource<?> cached = loadFromCache(key);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
return null; // 命中,直接返回
}
// ===== 两级内存缓存均未命中,准备异步加载 =====
// 3. 检查是否已经有一个相同 Key 的 EngineJob 正在执行(请求去重)
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
// 存在相同的任务,直接把自己的回调挂到这个任务上
// 当它完成后,所有注册的回调都会被通知,避免重复网络请求
current.addCallback(cb, callbackExecutor);
return new LoadStatus(cb, current);
}
// 4. 创建新的 EngineJob 和 DecodeJob
EngineJob<R> engineJob = engineJobFactory.build(...);
DecodeJob<R> decodeJob = decodeJobFactory.build(...);
// 5. 将任务注册到活动任务列表中
jobs.put(key, engineJob);
// 6. 将回调注册到 EngineJob
engineJob.addCallback(cb, callbackExecutor);
// 7. 启动!将 DecodeJob 投入线程池执行
engineJob.start(decodeJob);
return new LoadStatus(cb, engineJob);
}
下图完整展现了 Engine.load() 的决策路径:
flowchart TD
A["Engine.load()"] --> B["构建 EngineKey<br />(model + size + transforms + ...)"]
B --> C{"查找 ActiveResources<br />(WeakReference Map)"}
C -- "命中" --> D["acquire() 引用+1<br />直接回调 onResourceReady"]
C -- "未命中" --> E{"查找 MemoryCache<br />(LruResourceCache)"}
E -- "命中" --> F["从 LruCache 移除<br />放入 ActiveResources<br />acquire() 引用+1"]
F --> D
E -- "未命中" --> G{"是否存在相同 Key<br />的 EngineJob?"}
G -- "存在 (请求去重)" --> H["将自己的 callback<br />挂到已有 EngineJob 上"]
G -- "不存在" --> I["创建 EngineJob<br />创建 DecodeJob"]
I --> J["engineJob.start(decodeJob)"]
J --> K{"decodeJob.willDecodeFromCache()?"}
K -- "是" --> L["提交到 diskCacheExecutor"]
K -- "否" --> M["提交到 sourceExecutor"]
style D fill:#4CAF50,color:#fff
style H fill:#FF9800,color:#fff
关键设计:请求去重(Request Deduplication)。 当 RecyclerView 快速滑动时,可能会有多个 ViewHolder 请求同一张图片的不同尺寸版本。
EngineJob通过EngineKey进行去重——如果已经有一个相同的请求在执行中,后续请求不会再发起网络调用,而是挂载自己的回调等待通知。
四、 DecodeJob 状态机:数据获取的核心引擎
DecodeJob 实现了 Runnable 接口,被提交到线程池后,它的 run() 方法内部驱动着一个精密的状态机,逐级遍历各种数据源,直到找到可用数据。
4.1 RunReason 与 Stage
DecodeJob 有两个关键的枚举来控制其行为:
// DecodeJob.java
private enum RunReason {
INITIALIZE, // 首次启动,从 Stage.INITIALIZE 开始
SWITCH_TO_SOURCE_SERVICE, // 缓存未命中,需要切换到 SourceExecutor 线程池去拉取网络数据
DECODE_DATA, // 数据已获取成功,需要进行解码和变换
}
private enum Stage {
INITIALIZE, // 初始态
RESOURCE_CACHE, // 查找磁盘上已变换过的资源缓存
DATA_CACHE, // 查找磁盘上的原始数据缓存
SOURCE, // 从网络或本地源获取原始数据
ENCODE, // 将结果编码写入磁盘缓存
FINISHED, // 全部完成
}
4.2 runWrapped():状态机的核心驱动方法
// DecodeJob.java 源码片段
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
// 根据 DiskCacheStrategy 决定第一个要尝试的 Stage
stage = getNextStage(Stage.INITIALIZE);
// 获取该 Stage 对应的 DataFetcherGenerator
currentGenerator = getNextGenerator();
// 开始执行
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
// 所有磁盘缓存均未命中,已切换到 SourceExecutor 线程池
runGenerators();
break;
case DECODE_DATA:
// DataFetcher 已经拿到了原始数据,进行解码
decodeFromRetrievedData();
break;
}
}
private void runGenerators() {
boolean isStarted = false;
while (!isCancelled && currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
// 当前 Generator 没有数据,推进到下一个 Stage
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
// 如果推进到了 SOURCE 阶段,且当前线程不是 SourceExecutor
// 需要重新调度到 SourceExecutor 线程池
if (stage == Stage.SOURCE) {
reschedule(); // 设置 runReason = SWITCH_TO_SOURCE_SERVICE
return; // 退出当前线程,在新线程池中重新执行
}
}
}
下图完整展现了 DecodeJob 在各个 Stage 之间的状态流转:
stateDiagram-v2
[*] --> INITIALIZE : DecodeJob.run() 被线程池调用
INITIALIZE --> RESOURCE_CACHE : getNextStage()<br />(如果 diskCacheStrategy 允许查找变换后的缓存)
INITIALIZE --> DATA_CACHE : getNextStage()<br />(如果策略跳过 RESOURCE_CACHE)
INITIALIZE --> SOURCE : getNextStage()<br />(如果策略跳过所有磁盘缓存)
RESOURCE_CACHE --> DATA_CACHE : ResourceCacheGenerator<br />未命中
DATA_CACHE --> SOURCE : DataCacheGenerator<br />未命中
SOURCE --> ENCODE : SourceGenerator 成功获取数据<br />解码 → 变换完成
note right of SOURCE
⚠️ 进入 SOURCE 阶段时,
如果当前在 DiskCacheExecutor 线程上,
需要 reschedule() 到 SourceExecutor。
这就是 SWITCH_TO_SOURCE_SERVICE 的含义。
end note
ENCODE --> FINISHED : 将变换后的结果<br />写入磁盘缓存
FINISHED --> [*] : 回调 EngineJob.onResourceReady()
4.3 三种 DataFetcherGenerator
每个 Stage 对应一个 DataFetcherGenerator,它负责构造合适的 DataFetcher 去获取数据:
| Stage | Generator | 职责 |
|---|---|---|
RESOURCE_CACHE |
ResourceCacheGenerator |
从磁盘缓存中查找已经过降采样和 Transformation 处理的最终资源 |
DATA_CACHE |
DataCacheGenerator |
从磁盘缓存中查找原始的、未处理过的数据 |
SOURCE |
SourceGenerator |
从网络、本地文件或 ContentProvider 等原始数据源获取数据 |
五、 编解码层:Registry 与组件注册表
Glide 之所以能加载 URL、File、Uri、Byte 数组、甚至自定义 Model,靠的就是其强大的**组件注册表(Registry)**系统。Registry 是一个类型安全的多级映射表,它将 Model → Data → Resource → Transcode 的每一步都拆解为可插拔的组件。
flowchart LR
A["Model<br />(String URL / File / Uri)"] -- "ModelLoader" --> B["Data<br />(InputStream / ByteBuffer)"]
B -- "ResourceDecoder" --> C["Resource<br />(Bitmap / BitmapDrawable)"]
C -- "Transformation" --> D["Transformed Resource"]
D -- "ResourceTranscoder" --> E["Transcode Target<br />(Drawable)"]
style A fill:#E3F2FD
style B fill:#FFF3E0
style C fill:#E8F5E9
style D fill:#F3E5F5
style E fill:#FCE4EC
5.1 ModelLoader 与 DataFetcher:数据抓取双剑客
ModelLoader 是一个工厂接口,负责判断自己能否处理某种类型的 Model(如 String URL),并构造出对应的 DataFetcher。
// ModelLoader.java 接口定义
public interface ModelLoader<Model, Data> {
// 构建一个 LoadData 对象,内部包含 DataFetcher 和缓存 Key
@Nullable LoadData<Data> buildLoadData(
@NonNull Model model, int width, int height, @NonNull Options options);
// 判断此 Loader 是否能处理给定的 Model
boolean handles(@NonNull Model model);
}
// DataFetcher.java 接口定义 —— 真正的数据获取者
public interface DataFetcher<T> {
// 执行实际的数据获取(网络请求、文件读取等)
void loadData(@NonNull Priority priority, @NonNull DataCallback<? super T> callback);
// 取消获取操作
void cancel();
// 清理资源(关闭流等)
void cleanup();
// 返回数据的类型
@NonNull Class<T> getDataClass();
// 返回数据来源(LOCAL, REMOTE 等),影响缓存策略
@NonNull DataSource getDataSource();
}
以网络图片为例,默认的调用链是:
StringLoader将 URL 字符串转为UriHttpUriLoader将Uri转为GlideUrlHttpGlideUrlLoader创建HttpUrlFetcherHttpUrlFetcher.loadData()通过HttpURLConnection发起网络请求,返回InputStream
5.2 ResourceDecoder:从字节流到 Bitmap
拿到 InputStream 后,ResourceDecoder 负责将其解码为 Resource<Bitmap>。核心实现是 StreamBitmapDecoder → Downsampler。
Downsampler 是 Glide 中最精密的组件之一,它的核心算法是两阶段解码:
// Downsampler.java 核心逻辑(简化)
public Resource<Bitmap> decode(InputStream is, int requestedWidth, int requestedHeight, ...) {
// ===== 阶段一:只读取图片的元信息(宽高、格式),不解码像素 =====
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; // 关键!不分配内存
BitmapFactory.decodeStream(is, null, options);
// 此时 options.outWidth 和 options.outHeight 已被填充
// ===== 阶段二:计算采样率并解码 =====
// 计算 inSampleSize(例如:原图 4000x3000,目标 400x300 → inSampleSize = 8)
options.inSampleSize = calculateSampleSize(
options.outWidth, options.outHeight, requestedWidth, requestedHeight);
options.inJustDecodeBounds = false;
// 从 BitmapPool 尝试获取一块可复用的旧 Bitmap
options.inBitmap = bitmapPool.getDirty(
targetWidth, targetHeight, expectedConfig);
// 正式解码,像素数据直接写入 inBitmap 指向的内存块
Bitmap result = BitmapFactory.decodeStream(is, null, options);
return BitmapResource.obtain(result, bitmapPool);
}
inBitmap的精髓:在 Android 4.4(API 19)及以上版本中,只要新 Bitmap 需要的字节数 ≤ 旧 Bitmap 已分配的字节数,就可以直接复用。这也是BitmapPool存在的根本意义。
六、 极致的多级缓存架构
Glide 拥有可能是 Android 开源库中最精密的缓存体系。相比于传统的"内存 → 磁盘 → 网络"三层模型,Glide 引入了引用计数和活动资源追踪,构建了一个立体的内存管理屏障。
6.1 资源的生命周期流转
一个 Bitmap 资源在 Glide 内部会经历完整的生命旅程:
flowchart TD
A["网络/本地源<br />首次加载"] -- "解码 + 变换" --> B["EngineResource<br />(引用计数 = 1)"]
B -- "存入" --> C["ActiveResources<br />(WeakReference Map)"]
C -- "ImageView 展示中<br />acquire() 引用+1" --> C
C -- "ImageView 被回收<br />release() 引用-1<br />引用计数归零" --> D["MemoryCache<br />(LruResourceCache)"]
D -- "再次命中" --> C
D -- "LRU 淘汰" --> E{"onItemEvicted()"}
E -- "Bitmap" --> F["BitmapPool<br />(LruBitmapPool)<br />等待 inBitmap 复用"]
E -- "非 Bitmap 资源" --> G["recycle() 回收"]
F -- "BitmapPool 也满了" --> H["Bitmap.recycle()<br />彻底释放原生内存"]
style C fill:#4CAF50,color:#fff
style D fill:#2196F3,color:#fff
style F fill:#FF9800,color:#fff
style H fill:#f44336,color:#fff
6.2 ActiveResources 的引用计数与安全网
ActiveResources 内部维护了一个 HashMap<Key, ResourceWeakReference>。每个 ResourceWeakReference 持有底层 EngineResource 的弱引用。
EngineResource 内部的引用计数逻辑:
// EngineResource.java 源码片段
class EngineResource<Z> implements Resource<Z> {
private int acquired; // 引用计数器
private boolean isRecycled; // 是否已被回收
// 增加引用
synchronized void acquire() {
if (isRecycled) {
throw new IllegalStateException("Cannot acquire a recycled resource");
}
++acquired;
}
// 释放引用
void release() {
boolean release = false;
synchronized (this) {
if (acquired <= 0) {
throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
}
if (--acquired == 0) {
release = true;
}
}
if (release) {
// 引用计数归零,通知 Engine 将资源从 ActiveResources 移入 MemoryCache
listener.onResourceReleased(key, this);
}
}
}
当 release 为 true 时,Engine.onResourceReleased() 会执行以下操作:
// Engine.java
public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
// 1. 从 ActiveResources 中移除
activeResources.deactivate(cacheKey);
if (resource.isMemoryCacheable()) {
// 2. 放入 LruResourceCache
cache.put(cacheKey, resource);
} else {
// 3. 如果设置了 skipMemoryCache(true),直接回收
resourceRecycler.recycle(resource, /*forceNextFrame=*/ false);
}
}
安全网机制:ReferenceQueue
ActiveResources 内部还启动了一个后台清理线程,持续监听 ReferenceQueue。如果某个 EngineResource 被 GC 回收了(比如开发者在某些极端场景绕过了正常的 release() 流程),弱引用对象会被自动加入 ReferenceQueue。清理线程发现后,会执行兜底的资源回收,防止内存泄漏:
// ActiveResources.java
final class ActiveResources {
private final ReferenceQueue<EngineResource<?>> resourceReferenceQueue = new ReferenceQueue<>();
// 后台线程循环执行
void cleanReferenceQueue() {
while (!isShutdown) {
try {
// 阻塞等待,直到有弱引用被 GC 回收
ResourceWeakReference ref = (ResourceWeakReference) resourceReferenceQueue.remove();
cleanupActiveReference(ref);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
6.3 BitmapPool:享元模式杜绝内存抖动
在解码图片时,如果不断地 new Bitmap(),会导致 Java 堆内存迅速膨胀,引发频繁的 GC(内存抖动),导致 UI 卡顿。
Glide 的 LruBitmapPool 实现了经典的享元模式(Object Pool),其内部策略根据 Android 版本有所不同:
- API ≥ 19 (KitKat):使用
SizeStrategy,按字节大小索引。只要新 Bitmap 的字节数 ≤ 候选池中某个旧 Bitmap 的字节数,就可以复用(通过inBitmap)。 - API < 19:使用
AttributeStrategy,要求宽、高、Config 完全匹配才能复用。
// LruBitmapPool.java 核心逻辑
public Bitmap getDirty(int width, int height, Bitmap.Config config) {
// 从内部策略中查找一块可复用的 Bitmap
final Bitmap result = strategy.get(width, height,
config != null ? config : DEFAULT_CONFIG);
if (result == null) {
// 池中没有合适的,只能 new 一个新的
return Bitmap.createBitmap(width, height, config);
}
return result; // 返回旧 Bitmap,像素数据是 "脏" 的,稍后会被 decodeStream 覆写
}
七、 线程池隔离调度
Glide 为不同性质的任务分配了独立的线程池 (GlideExecutor),采用物理隔离策略防止不同类型的 IO 操作相互阻塞:
flowchart LR
subgraph "GlideExecutor 线程池集群"
A["DiskCacheExecutor<br />🖴 磁盘读取<br />核心线程数: 1<br />阻塞时间短"]
B["SourceExecutor<br />🌐 网络请求<br />核心线程数: CPU cores<br />阻塞时间长"]
C["UnlimitedSourceExecutor<br />🌐🌐 无限网络<br />无核心线程上限<br />用于紧急场景"]
D["AnimationExecutor<br />🎬 GIF 动画<br />核心线程数: 1~2<br />持续解码帧"]
end
E["DecodeJob"] -- "从磁盘缓存解码" --> A
E -- "从网络下载" --> B
E -- "UNLIMITED 策略" --> C
F["AnimationDecoder"] --> D
为什么要隔离?
- 假设只有一个公共线程池,4 个线程都在进行网络下载(每个耗时 3 秒),这时候一个已经在磁盘缓存中的图片也需要加载,它必须排队等那 4 个网络任务完成才能执行,导致用户看到一个本该瞬间显示的图片卡了 3 秒。
- 物理上分离磁盘读取和网络请求的线程池,可以保证磁盘缓存中的图片总是能被快速加载,不受网络拥塞影响。
GlideExecutor 内部使用 PriorityBlockingQueue 作为任务队列,高优先级的请求(如当前可见的 ImageView)会被优先处理,进一步保证了用户体验。
八、 总结:工程化设计模式的教科书
纵观 Glide 的源码架构,我们可以提炼出多个教科书级别的系统设计模式:
| 设计模式 | Glide 中的体现 | 核心价值 |
|---|---|---|
| 享元模式 | BitmapPool 池化复用 Bitmap 内存 |
彻底消除内存抖动 |
| 观察者模式 | ActivityFragmentLifecycle → RequestManager |
生命周期事件透传 |
| 策略模式 | DiskCacheStrategy / DownsampleStrategy |
可替换的算法族 |
| 责任链模式 | ModelLoader → DataFetcher 的注册表遍历 |
可扩展的数据源支持 |
| 适配器模式 | ViewTarget 将 ImageView 适配为统一的 Target |
解耦View类型依赖 |
| 状态机模式 | DecodeJob 的 Stage/RunReason 流转 |
复杂异步流程的可控管理 |
| 引用计数 | EngineResource.acquire()/release() |
精确的资源生命周期管理 |
研究 Glide 的源码,其核心价值在于学习如何在极端资源受限的移动端环境下,通过精妙的数据结构(WeakReference + ReferenceQueue 兜底、LruCache 双缓冲队列)、引用计数(EngineResource 的 acquire/release 语义)和并发控制(多维线程池物理隔离 + PriorityBlockingQueue),写出健壮、高效且丝滑的系统级框架代码。