题目
设计高性能图片加载框架并解决内存抖动问题
信息
- 类型:问答
- 难度:⭐⭐⭐
考点
Bitmap内存管理,内存抖动优化,三级缓存设计,弱引用与LRU策略,Glide源码原理
快速回答
实现高性能图片加载框架的关键点:
- 采用三级缓存架构:内存缓存(LruCache + 弱引用)、磁盘缓存、网络加载
- 使用LruCache管理Bitmap内存,结合弱引用防止内存泄漏
- 通过Bitmap复用池(inBitmap)减少内存分配
- 采用对象池模式复用请求对象,避免频繁GC
- 根据View尺寸进行采样压缩(inSampleSize + inJustDecodeBounds)
核心架构设计
三级缓存流程:
- 检查活动缓存(弱引用持有正在使用的Bitmap)
- 检查LruCache内存缓存
- 检查磁盘缓存(DiskLruCache)
- 网络下载并写入各级缓存
内存抖动优化方案
// Bitmap复用池实现
public class BitmapPool {
private final Set<Bitmap> pool = Collections.synchronizedSet(new HashSet<>());
public Bitmap get(int width, int height) {
for (Bitmap bmp : pool) {
if (bmp.isRecycled()) continue;
if (bmp.getWidth() == width && bmp.getHeight() == height) {
pool.remove(bmp);
return bmp;
}
}
return null;
}
public void put(Bitmap bitmap) {
if (bitmap != null && !bitmap.isRecycled()) {
pool.add(bitmap);
}
}
}关键优化技术
- LruCache 实现:
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); int cacheSize = maxMemory / 8; LruCache<String, Bitmap> memoryCache = new LruCache<>(cacheSize) { protected int sizeOf(String key, Bitmap value) { return value.getByteCount() / 1024; } protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) { bitmapPool.put(oldValue); // 移出时放入复用池 } }; - 采样压缩:
先读取图片边界(inJustDecodeBounds=true),根据View尺寸计算inSampleSize - 弱引用缓存:
private Map<String, WeakReference<Bitmap>> activeCache = new HashMap<>();
最佳实践
- 使用RGB_565格式替代ARGB_8888(内存减少50%)
- 监听onTrimMemory动态调整缓存大小
- 网络层使用OkHttp连接池复用TCP连接
- 磁盘缓存采用Journal日志保证数据一致性
常见错误
- 未处理Configuration变更导致重复加载
- ListView/RecyclerView中未使用ViewTag引发图片错位
- 在主线程解码大图导致ANR
- 弱引用缓存未与LruCache联动造成内存泄漏
扩展知识
- Glide源码设计:
- EngineJob协调加载流程
- ResourceRecycler管理Bitmap生命周期
- Downsampler处理采样逻辑
- Android 8.0后:使用NativeAllocationRegistry替代inBitmap
- 内存抖动监控:通过Allocation Tracker分析分配模式