首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Glide 图片加载原理:三级缓存、生命周期绑定与内存优化

Glide 图片加载原理:三级缓存、生命周期绑定与内存优化

原创
作者头像
hunter android
发布于 2026-10-26 11:03:05
发布于 2026-10-26 11:03:05
280
举报

Glide 图片加载原理:三级缓存、生命周期绑定与内存优化

图片加载是 Android 开发中最常见的需求之一,但直接用 BitmapFactory 很快会遇到内存溢出、重复加载、生命周期管理等问题。Glide 作为 Google 推荐的图片加载库,通过三级缓存、生命周期绑定和内存优化策略,优雅地解决了这些痛点。

本文深入 Glide 的核心机制,从源码层面拆解它如何管理图片的加载、缓存与资源回收。


为什么不直接用 BitmapFactory?

直接加载图片面临的典型问题:

代码语言:javascript
复制
// ❌ 常见错误写法
val bitmap = BitmapFactory.decodeFile("/sdcard/large_image.jpg")
imageView.setImageBitmap(bitmap)

问题 1:内存溢出 一张 4000×3000 的图片,ARGB_8888 格式需要 4000 * 3000 * 4 = 48MB 内存,很容易触发 OOM。

问题 2:主线程阻塞 网络图片加载在主线程会导致 ANR。

问题 3:生命周期管理 Activity 已销毁时,回调设置 Bitmap 会导致内存泄漏或崩溃。

问题 4:重复加载 同一个 URL 在列表中反复加载,浪费流量和内存。

Glide 针对性地解决了这些问题。


Glide 的核心架构

Glide 的加载流程可以简化为:

代码语言:javascript
复制
Glide.with(context)
  .load(url)
  .into(imageView)

背后经历了 生命周期感知 → 缓存检查 → 解码 → 转换 → 显示 的完整链路。

1. 生命周期绑定

Glide 通过隐藏的 Fragment 监听生命周期:

代码语言:javascript
复制
// RequestManagerFragment.java (Glide 内部)
public class RequestManagerFragment extends Fragment {
  private final ActivityFragmentLifecycle lifecycle;

  @Override
  public void onStart() {
    super.onStart();
    lifecycle.onStart();
  }

  @Override
  public void onStop() {
    super.onStop();
    lifecycle.onStop();
  }

  @Override
  public void onDestroy() {
    super.onDestroy();
    lifecycle.onDestroy();
  }
}

关键设计: - 调用 Glide.with(activity) 时,Glide 会向当前 Activity 添加一个无 UI 的 Fragment - 当 Activity 进入 onStop 时,Glide 暂停所有请求 - 当 Activity 被销毁时,自动取消请求并清理资源

这样开发者无需手动管理请求的生命周期,避免了内存泄漏。


三级缓存策略

Glide 的缓存分为三层:活动资源缓存(Active Resources) → 内存缓存(Memory Cache) → 磁盘缓存(Disk Cache)。

活动资源缓存(强引用)

正在使用的图片通过强引用保存在 Map<Key, WeakReference<Resource>> 中,避免被 GC 回收:

代码语言:javascript
复制
// ActiveResources.java
final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();

synchronized void activate(Key key, EngineResource<?> resource) {
  ResourceWeakReference toPut = new ResourceWeakReference(key, resource, getReferenceQueue());
  activeEngineResources.put(key, toPut);
}

特点: - 使用中的图片不会被复用或清理 - 引用计数归零后移入内存缓存

内存缓存(LruCache)

使用 LRU(Least Recently Used)策略管理内存:

代码语言:javascript
复制
// LruResourceCache.java
private final Map<T, Entry<Y>> cache = new LinkedHashMap<>(100, 0.75f, true);

public synchronized Y put(T key, Y item) {
  final int itemSize = getSize(item);
  if (itemSize >= maxSize) {
    return null; // 单个资源过大,不缓存
  }

  currentSize += itemSize;
  Entry<Y> old = cache.put(key, new Entry<>(item, itemSize));

  if (old != null) {
    currentSize -= old.size;
  }

  evict(); // 超过限制时清理旧资源
  return old != null ? old.value : null;
}

内存限制计算:

代码语言:javascript
复制
// MemorySizeCalculator.java
int targetMemoryCacheSize = (int) (maxSize * memoryCacheScreens);
// 默认保留 2 个屏幕的图片

磁盘缓存(DiskLruCache)

网络图片的原始数据和处理后的结果都会缓存到磁盘:

代码语言:javascript
复制
// 配置磁盘缓存策略
Glide.with(this)
  .load(url)
  .diskCacheStrategy(DiskCacheStrategy.ALL) // 原图 + 结果
  .into(imageView)

DiskCacheStrategy 选项: - NONE:不缓存 - RESOURCE:只缓存转换后的图片 - DATA:只缓存原始图片 - ALL:缓存原始图片和转换后的结果(默认) - AUTOMATIC:智能选择策略

磁盘缓存使用 DiskLruCache 实现,通过 SHA-256 的 URL 生成缓存 Key:

代码语言:javascript
复制
// DataCacheKey.java
@Override
public String toString() {
  return "DataCacheKey{"
      + "sourceKey=" + sourceKey
      + ", signature=" + signature
      + '}';
}

内存优化核心策略

1. 图片降采样(Downsampling)

加载大图时自动计算采样率:

代码语言:javascript
复制
// Downsampler.java
int inSampleSize = calculateScaling(...);

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; // 只读尺寸,不加载像素
BitmapFactory.decodeStream(inputStream, null, options);

options.inSampleSize = inSampleSize;
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options);

示例: - 原图 4000×3000,目标 View 400×300 - 采样率 inSampleSize = 8,实际加载 500×375 的图片 - 内存占用从 48MB 降至 0.75MB

2. Bitmap 复用(BitmapPool)

Glide 维护一个 BitmapPool,复用已释放的 Bitmap 内存块:

代码语言:javascript
复制
// LruBitmapPool.java
public synchronized Bitmap getDirty(int width, int height, Bitmap.Config config) {
  Bitmap result = strategy.get(width, height, config);
  if (result != null) {
    currentSize -= strategy.getSize(result);
  }
  return result;
}

效果: 避免频繁的 Bitmap 创建和 GC,减少内存抖动。

3. 图片格式选择

Glide 默认使用 RGB_565(2 字节/像素)替代 ARGB_8888(4 字节/像素):

代码语言:javascript
复制
// 手动指定格式
Glide.with(this)
  .load(url)
  .format(DecodeFormat.PREFER_RGB_565)
  .into(imageView)

权衡: - RGB_565:不支持透明度,但内存占用减半 - ARGB_8888:支持透明度,适合 PNG


加载流程源码解析

完整的加载链路:

代码语言:javascript
复制
RequestBuilder.into()
  ↓
SingleRequest.begin()
  ↓
Engine.load()
  ↓
[检查活动资源] → [检查内存缓存] → [检查磁盘缓存] → [网络请求]
  ↓
DecodeJob.run() (在后台线程)
  ↓
Downsampler.decode()
  ↓
Transformation.transform() (如 CenterCrop)
  ↓
ResourceTranscoder.transcode()
  ↓
Target.onResourceReady() (回到主线程)

核心类职责

类

职责

RequestManager

管理请求生命周期

Engine

缓存查询与任务调度

DecodeJob

在后台线程解码图片

Downsampler

降采样逻辑

BitmapPool

Bitmap 复用池

Target

将资源设置到 View


常见使用场景与优化

1. 圆形裁剪

代码语言:javascript
复制
Glide.with(this)
  .load(url)
  .circleCrop()
  .into(imageView)

内部实现:

代码语言:javascript
复制
// CircleCrop.java
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
  return TransformationUtils.circleCrop(pool, toTransform, outWidth, outHeight);
}

2. 占位图与错误处理

代码语言:javascript
复制
Glide.with(this)
  .load(url)
  .placeholder(R.drawable.loading) // 加载中
  .error(R.drawable.error) // 加载失败
  .into(imageView)

3. 缩略图策略

代码语言:javascript
复制
Glide.with(this)
  .load(url)
  .thumbnail(0.1f) // 先加载 10% 尺寸的缩略图
  .into(imageView)

效果: 用户先看到模糊的预览图,提升体验。

4. 预加载

代码语言:javascript
复制
Glide.with(this)
  .load(url)
  .preload(500, 500)

场景: 在列表滑动前,提前加载即将显示的图片。


与 Picasso、Coil 的对比

特性

Glide

Picasso

Coil

支持 GIF

✅

❌

✅

Bitmap 复用

✅

❌

✅

生命周期绑定

自动

手动

自动

Kotlin 协程

通过扩展

❌

原生支持

磁盘缓存

DiskLruCache

OkHttp 缓存

OkHttp 缓存

结论: - Glide:功能最全,适合复杂场景 - Coil:Kotlin 优先,协程友好,体积小 - Picasso:简单易用,但功能相对受限


最佳实践建议

  1. 统一配置全局 Glide
代码语言:javascript
复制
@GlideModule
class MyAppGlideModule : AppGlideModule() {
  override fun applyOptions(context: Context, builder: GlideBuilder) {
    builder.setMemoryCache(LruResourceCache(20 * 1024 * 1024))
    builder.setDiskCache(InternalCacheDiskCacheFactory(context, 100 * 1024 * 1024))
  }
}
  1. 避免过度转换
代码语言:javascript
复制
// ❌ 每次都转换
Glide.with(this).load(url).circleCrop().into(imageView)

// ✅ 服务端返回已裁剪好的图片
Glide.with(this).load(croppedUrl).into(imageView)
  1. 列表优化
代码语言:javascript
复制
// RecyclerView 滑动监听
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
  override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
    if (newState == RecyclerView.SCROLL_STATE_IDLE) {
      Glide.with(this@MainActivity).resumeRequests()
    } else {
      Glide.with(this@MainActivity).pauseRequests()
    }
  }
})

总结

Glide 通过以下机制解决图片加载的核心问题:

  1. 三级缓存:活动资源 → 内存 LRU → 磁盘,减少重复加载
  2. 生命周期绑定:自动取消请求,避免内存泄漏
  3. 内存优化:降采样、Bitmap 复用、格式选择
  4. 灵活配置:支持占位图、转换、缩略图、预加载

理解这些原理,不仅能用好 Glide,也能在遇到自定义需求时,知道从哪个环节入手优化。


延伸思考: 如果项目需要加载超大图(如地图瓦片),Glide 的缓存策略可能不适用,需要考虑 SubsamplingScaleImageView 等方案。图片加载的优化,永远是在功能、性能和体验之间找平衡。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • Glide 图片加载原理:三级缓存、生命周期绑定与内存优化
    • 为什么不直接用 BitmapFactory?
    • Glide 的核心架构
      • 1. 生命周期绑定
    • 三级缓存策略
      • 活动资源缓存(强引用)
      • 内存缓存(LruCache)
      • 磁盘缓存(DiskLruCache)
    • 内存优化核心策略
      • 1. 图片降采样(Downsampling)
      • 2. Bitmap 复用(BitmapPool)
      • 3. 图片格式选择
    • 加载流程源码解析
      • 核心类职责
    • 常见使用场景与优化
      • 1. 圆形裁剪
      • 2. 占位图与错误处理
      • 3. 缩略图策略
      • 4. 预加载
    • 与 Picasso、Coil 的对比
    • 最佳实践建议
    • 总结
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档