一句话总结:
用快递驿站储物柜类比理解:
LruCache就像智能储物柜系统,它的运行规则是:
1. 每个快递(数据)都有固定大小的格子
2. 新快递来优先放空柜子
3. 柜子满了就淘汰最久没取的快递(LRU规则)
4. 经常取的快递位置会刷新到最新区域
一、核心原理揭秘
1. 数据结构核心 - 双向链表+哈希表
// 源码核心实现(简化版)
class LruCache<K, V>(maxSize: Int) {
private val cache = LinkedHashMap<K, V>(
0, 0.75f, true // true表示按访问顺序排序
)
// 当前缓存大小(字节/单位)
private var currentSize = 0
private val maxSize = maxSize
}
LinkedHashMap的魔法:
- 每次
get/put操作会把元素移到链表尾部 - 链表头部的元素就是最久未使用的
2. 淘汰算法动态演示
假设最大容量=3,操作顺序:A→B→C→A→D
操作步骤 当前缓存状态 淘汰情况
put(A) [A] 无
put(B) [A→B] 无
put(C) [A→B→C] 无
get(A) [B→C→A] 无
put(D) [C→A→D] 淘汰B
二、使用指南(三步搞定)
1. 基础初始化
// 创建图片缓存(最大10MB)
val imageCache = object : LruCache<String, Bitmap>(10 * 1024 * 1024) {
// 计算每个Bitmap的内存占用
override fun sizeOf(key: String, value: Bitmap): Int {
return value.allocationByteCount
}
// 元素被移除时的回调(可做资源回收)
override fun entryRemoved(evicted: Boolean, key: String,
oldValue: Bitmap, newValue: Bitmap?) {
oldValue.recycle()
}
}
2. 常用操作API
// 添加缓存
imageCache.put("cat.jpg", bitmap)
// 获取缓存(会更新元素位置)
val bitmap = imageCache.get("cat.jpg")
// 强制清理(保留最近50%的内容)
imageCache.trimToSize(imageCache.maxSize() / 2)
// 获取缓存快照(线程安全拷贝)
val snapshot = imageCache.snapshot()
3. 配合协程使用
// 异步加载并缓存
fun CoroutineScope.loadImageAsync(url: String) = async(Dispatchers.IO) {
var bitmap = imageCache.get(url)
if (bitmap == null) {
bitmap = downloadImage(url)
imageCache.put(url, bitmap)
}
return@async bitmap
}
四、避坑指南
坑1:哈希碰撞攻击
危险代码:
// 使用默认hashCode可能被恶意攻击
val cache = LruCache<HttpRequest, Response>(100)
安全方案:
// 启用安全哈希模式
val safeCache = LruCache<String, Data>(
maxSize = 100,
hasher = { key ->
Security.getSecureHash(key)
}
)
坑2:内存计算误差
错误实现:
override fun sizeOf(key: String, value: Bitmap): Int {
return 1 // 错误!导致实际内存超出限制
}
正确方案:
// 使用官方API获取真实内存占用
override fun sizeOf(key: String, value: Bitmap): Int {
return if (Build.VERSION.SDK_INT >= 33) {
value.allocationByteCount
} else {
value.byteCount
}
}
五、性能优化技巧
1. 分层缓存架构
// 三级缓存方案
fun loadImage(url: String): Bitmap? {
// 1. 查内存缓存
var bitmap = memoryCache.get(url)
if (bitmap != null) return bitmap
// 2. 查磁盘缓存
bitmap = diskLruCache.get(url)
if (bitmap != null) {
memoryCache.put(url, bitmap)
return bitmap
}
// 3. 网络下载
bitmap = download(url)
diskLruCache.put(url, bitmap)
memoryCache.put(url, bitmap)
return bitmap
}
2. 智能预加载
// 预测用户行为预加载
fun preloadImages(visibleItems: List<String>) {
val prediction = Predictor.analyzeScrollTrend()
visibleItems.forEachIndexed { index, url ->
if (index < prediction.loadAheadCount) {
launch {
val bitmap = imageCache.get(url) ?: download(url)
imageCache.put(url, bitmap)
}
}
}
}
LruCache终极口诀:
链表哈希双结构,最近最少先淘汰
容量计算要精准,entryRemoved做清理
线程安全需注意,协程封装更高效
内存磁盘分层存,三级架构性能强
预加载,智能清,2025用法要记牢!