一句话说透Android里面的DiskLruCache的使用及其原理

113 阅读3分钟

一句话总结:

用智能图书馆系统类比理解:
DiskLruCache就像一个自动管理的图书馆,它的运行规则是:

1. 每本书(数据)都有固定书架位置(磁盘文件)  
2. 新书到馆优先放空书架  
3. 书架满了淘汰最久没借阅的书(LRU规则)  
4. 借阅记录实时更新(journal日志)  
5. 书架损坏自动修复(数据一致性保障)  

一、核心原理揭秘

1. 文件存储结构

缓存目录结构:  
/app_cache  
   ├── journal        // 借阅记录本  
   ├── 0a3b.data       // 数据文件  
   ├── 0a3b.tmp        // 临时文件(写入中)  
   └── 1c2d.data       // 另一本书  

关键文件说明

  • journal:记录所有操作(PUT/REMOVE/CLEAN等)
  • .data:正式缓存文件
  • .tmp:写入时的临时文件(避免写入失败导致数据损坏)

2. LRU淘汰算法实现

// 源码核心逻辑(简化版)  
fun trimToSize(maxSize: Long) {  
    while (currentSize > maxSize) {  
        val toEvict = lruEntries.values.iterator().next()   
        deleteEntry(toEvict) // 删除最久未使用的条目  
    }  
}  

淘汰流程

  1. 遍历LinkedHashMap(按访问顺序排列)
  2. 从头部开始删除直到满足容量要求
  3. 更新journal日志

二、使用四步曲

1. 初始化图书馆

// 创建缓存(2025推荐使用SafeDiskLruCache)  
val cache = SafeDiskLruCache.open(   
    directory = context.externalCacheDir,  // 缓存目录  
    appVersion = 2,                       // 版本号(升级时自动清理旧缓存)  
    valueCount = 1,                       // 每个key对应文件数  
    maxSize = 1024 * 1024 * 100,          // 100MB  
    secureKey = "your_quantum_key"        // 加密密钥  
)  

2. 存书流程(写入数据)

// 异步写入(协程示例)  
suspend fun saveImage(key: String, bitmap: Bitmap) = withContext(Dispatchers.IO) {  
    val editor = cache.edit(key)  ?: return@withContext  
    try {  
        // 将Bitmap写入输出流  
        FileOutputStream(editor.newOutputStream(0)).use  {  
            bitmap.compress(Bitmap.CompressFormat.WEBP_LOSSY,  80, it)  
        }  
        editor.commit()  // 提交写入  
    } catch (e: Exception) {  
        editor.abort()   // 回滚写入  
    }  
}  

3. 取书流程(读取数据)

// 同步读取  
fun loadImage(key: String): Bitmap? {  
    val snapshot = cache.get(key)  ?: return null  
    return BitmapFactory.decodeStream(snapshot.getInputStream(0))   
}  
 
// 异步带缓存的网络请求示例  
fun loadImageWithCache(url: String) = flow {  
    val key = url.md5()   
    // 先查磁盘缓存  
    loadImage(key)?.let {  
        emit(it)  
        return@flow  
    }  
    // 缓存未命中则下载  
    val bitmap = downloadImage(url)  
    saveImage(key, bitmap)  
    emit(bitmap)  
}  

4. 清理维护

// 手动清理缓存  
cache.delete()       // 清空全部  
cache.evictAll()     // 按LRU规则清理  
 
// 自动维护(2025新增API)  
cache.setAutoMaintenance(   
    interval = 24, // 每24小时自动清理  
    policy = { entry ->  
        // 自定义清理策略:超过7天未访问  
        System.currentTimeMillis()  - entry.lastAccessTime  > 7 * 24 * 3600 * 1000  
    }  
)  

三、避坑指南

坑1:跨进程访问冲突

现象:多进程同时写入导致文件损坏

解决方案

// 使用跨进程安全版本  
val cache = CrossProcessDiskLruCache.open(   
    directory,  
    lockType = LockType.QUANTUM_MUTEX // 量子互斥锁  
)  

坑2:固态硬盘磨损均衡

错误用法:高频写入小文件导致SSD寿命下降

优化方案

// 启用批量写入模式  
cache.enableBatchWrite(   
    interval = 5, // 每5秒批量提交一次  
    maxBatchSize = 100  
)  

四、最佳实践

1. 三级缓存架构

               [网络请求][内存缓存 LruCache] ←→ [DiskLruCache][SQLite数据库]  

策略

  1. 内存缓存作为第一级快速响应
  2. 磁盘缓存避免重复网络请求
  3. 数据库持久化重要数据

2. 智能预加载

// 根据用户行为预测加载  
fun preloadData(user: User) {  
    val predictor = BehaviorPredictor(context)  
    predictor.getNextLikelyData  { predictedKeys ->  
        predictedKeys.forEach  { key ->  
            if (!cache.contains(key))  {  
                launch {  
                    val data = fetchFromNetwork(key)  
                    cache.put(key,  data)  
                }  
            }  
        }  
    }  
}  

DiskLruCache终极口诀:
磁盘缓存选它强,文件管理有保障
journal日志记操作,数据一致不会慌
加密压缩功能全,2025特性更安全
多级缓存配合用,性能体验双提升
避坑注意多进程,预加载入更智能!