Glide 的 BitmapPool 实现原理分析
1. 概述
BitmapPool 是 Glide 内存优化的核心组件之一,主要目的是复用 Bitmap 内存,减少 GC 和内存分配开销,提升应用性能。
2. 主要作用
- 减少内存分配:复用已分配的 Bitmap 内存
- 降低 GC 频率:避免频繁创建和回收 Bitmap
- 提升性能:减少系统级的内存分配调用
- 防止 OOM:通过合理的复用策略管理内存
3. 核心实现类:LruBitmapPool
3.1 主要数据结构
java
public class LruBitmapPool implements BitmapPool {
private final LruPoolStrategy strategy; // 存储策略
private final Set<Bitmap> allowList; // 白名单(某些Bitmap不放入池)
private final long initialMaxSize; // 初始最大容量
private long maxSize; // 当前最大容量
private long currentSize; // 当前已使用大小
private int hits, misses; // 命中统计
private int puts; // 放入统计
private int evictions; // 驱逐统计
}
3.2 存储策略(LruPoolStrategy)
根据 Android 版本使用不同策略:
java
// Android 4.4+ 使用 SizeConfigStrategy
static LruPoolStrategy getDefaultStrategy() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
return new SizeConfigStrategy();
} else {
return new AttributeStrategy();
}
}
SizeConfigStrategy(Android 4.4+):
- 按 Bitmap 配置(ARGB_8888, RGB_565等)和大小分组
- 使用
GroupedLinkedMap存储相同配置的 Bitmap - 支持复用更大尺寸的 Bitmap(需重新配置)
AttributeStrategy(Android 4.4以下):
- 按 width、height、config 精确匹配
- 使用简单 HashMap 存储
4. 关键方法实现
4.1 get() - 获取 Bitmap
java
@Override
public Bitmap get(int width, int height, Bitmap.Config config) {
Bitmap result = getDirtyOrNull(width, height, config);
if (result != null) {
// 清零Bitmap数据
result.eraseColor(Color.TRANSPARENT);
} else {
// 池中没有合适的,创建新的
result = createBitmap(width, height, config);
}
return result;
}
private Bitmap getDirtyOrNull(int width, int height, Bitmap.Config config) {
// 1. 参数校验
Preconditions.checkArgument(width > 0 && height > 0);
// 2. 获取合适的Bitmap
Bitmap result = strategy.get(width, height, config);
if (result == null) {
misses++;
} else {
hits++;
currentSize -= strategy.getSize(result);
// 从allowList移除
allowList.remove(result);
// 重新配置Bitmap
normalize(result);
}
return result;
}
4.2 put() - 放入 Bitmap
java
@Override
public void put(Bitmap bitmap) {
// 1. 参数校验
Preconditions.checkNotNull(bitmap);
Preconditions.checkArgument(bitmap.isMutable());
Preconditions.checkArgument(bitmap.getWidth() > 0
&& bitmap.getHeight() > 0);
// 2. 计算大小并尝试放入
final int size = strategy.getSize(bitmap);
strategy.put(bitmap);
puts++;
currentSize += size;
// 3. 执行LRU清理
evict();
}
private void evict() {
trimToSize(maxSize);
}
private void trimToSize(long size) {
while (currentSize > size) {
// 移除最近最少使用的Bitmap
final Bitmap removed = strategy.removeLast();
if (removed == null) {
break;
}
currentSize -= strategy.getSize(removed);
removed.recycle();
evictions++;
}
}
5. 内存管理策略
5.1 大小计算
java
private static int getBitmapByteSize(int width, int height,
Bitmap.Config config) {
return width * height * getBytesPerPixel(config);
}
private static int getBytesPerPixel(Bitmap.Config config) {
switch (config) {
case ARGB_8888:
return 4;
case RGB_565:
case ARGB_4444:
return 2;
case ALPHA_8:
return 1;
default:
return 1;
}
}
5.2 自适应大小调整
java
public synchronized void setSizeMultiplier(float sizeMultiplier) {
maxSize = Math.round(initialMaxSize * sizeMultiplier);
evict(); // 调整后立即清理
}
6. 与 Glide 其他组件的协作
6.1 与 MemoryCache 的关系
java
// 在Engine中协调使用
public class Engine implements EngineJobListener {
private final MemoryCache memoryCache; // 存储解码后的图片
private final BitmapPool bitmapPool; // 存储可复用的Bitmap内存
// 当从MemoryCache移除时,放入BitmapPool
private void onResourceReleased(Key key, Resource resource) {
if (resource.isCacheable()) {
memoryCache.put(key, resource);
} else {
bitmapPool.put(resource.get().getBitmap());
}
}
}
6.2 与 Downsampler 的协作
java
// Downsampler使用BitmapPool复用内存
private static Bitmap decodeStream(InputStream is, BitmapPool pool,
DecodeFormat decodeFormat) {
// 从pool获取Bitmap
Bitmap result = pool.get(options.outWidth, options.outHeight,
options.inPreferredConfig);
// 解码到复用的Bitmap中
return BitmapFactory.decodeStream(is, null, options);
}
7. 性能优化技巧
7.1 Bitmap 复用条件
- Mutable:必须是可变的 Bitmap
- Size >= Requested:尺寸必须大于等于请求尺寸
- Config Compatible:配置兼容(可向下兼容)
- Reusable:Android 4.4+ 支持复用任意配置的 Bitmap
7.2 配置建议
java
// 在GlideModule中配置
public class MyGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
// 根据设备内存动态设置
ActivityManager am = (ActivityManager)
context.getSystemService(Context.ACTIVITY_SERVICE);
boolean isLargeHeap = (context.getApplicationInfo().flags
& ApplicationInfo.FLAG_LARGE_HEAP) != 0;
int memoryCacheSize = isLargeHeap ? 256 : 128;
int bitmapPoolSize = isLargeHeap ? 128 : 64;
builder.setMemoryCache(
new LruResourceCache(memoryCacheSize * 1024 * 1024));
builder.setBitmapPool(
new LruBitmapPool(bitmapPoolSize * 1024 * 1024));
}
}
8. 注意事项
- 线程安全 :
LruBitmapPool使用synchronized保证线程安全 - 内存计算:精确计算 Bitmap 内存占用
- 配置兼容:注意不同 Android 版本的差异
- 白名单机制:某些特殊 Bitmap 不放入池中
9. 总结
Glide 的 BitmapPool 通过精妙的 LRU 策略和版本适配,实现了高效的 Bitmap 内存复用:
- 减少 80%+ 的 Bitmap 分配:显著降低 GC 压力
- 智能匹配策略:根据配置和尺寸灵活复用
- 动态内存管理:根据应用状态调整池大小
- 版本兼容:适配不同 Android 版本的特性
这种设计使得 Glide 在加载大量图片时仍能保持流畅的性能和低内存占用。