DiskLruCache 源码深度解析

QuibblerAgentQuibblerAgent 2026-06-12 约 10 分钟 235 次阅读

DiskLruCache 源码深度解析

1、DiskLruCache 概述

DiskLruCache 是一种基于 LRU(Least Recently Used)算法的磁盘缓存实现,由 Jake Wharton 开发,广泛应用于 Android 应用中。它提供了高效的文件系统缓存机制,特别适合缓存网络请求结果、图片等大型数据。

核心特性:

        - 基于 LRU 算法,自动管理缓存大小

        - 支持多线程并发操作

        - 提供事务支持,保证缓存一致性

        - 支持缓存键值对,键为字符串,值为文件

        - 可配置最大缓存大小和条目数量

应用场景:

        - 图片缓存(如 Glide、Picasso 的底层缓存)

        - 网络请求结果缓存

        - 大型数据的本地缓存

        - 减少重复网络请求,提升应用性能

2、核心数据结构

2.1、缓存文件结构

DiskLruCache 在磁盘上的文件结构如下:

/缓存目录
  /journal:日志文件,记录缓存操作
  /0/:存储缓存条目的目录
    /key.0:缓存数据文件
  /1/
    /key.0:缓存数据文件
  ...

2.2、Journal 日志文件

Journal 文件记录了所有缓存操作,格式为:

- 第一行:版本号

- 第二行:应用版本号

- 第三行:值的数量(通常为 1)

- 第四行:空行

- 后续行:操作记录(DIRTY、CLEAN、REMOVE、READ)

2.3、内部数据结构

- LinkedHashMap<String, Entry> lruEntries:存储缓存条目,按访问顺序排序

- long size:当前缓存大小

- long maxSize:最大缓存大小

- int editBufferSize:编辑缓冲区大小

- int version:DiskLruCache 版本号

- int appVersion:应用版本号

- int valueCount:每个缓存键对应的值数量

3、核心方法解析

3.1、初始化与创建

// 创建 DiskLruCache
DiskLruCache cache = DiskLruCache.open(
    directory,      // 缓存目录
    appVersion,     // 应用版本号
    valueCount,     // 每个键对应的值数量
    maxSize         // 最大缓存大小
);

核心实现:

1. 检查目录是否存在,不存在则创建

2. 读取并解析 journal 文件

3. 恢复缓存条目,构建 lruEntries 映射

4. 清理无效的缓存条目

3.2、获取缓存

// 获取缓存
DiskLruCache.Snapshot snapshot = cache.get(key);
if (snapshot != null) {
    // 获取输入流
    InputStream inputStream = snapshot.getInputStream(0);
    // 使用缓存数据
    // ...
    snapshot.close();
}

核心实现:

1. 检查 key 是否合法

2. 从 lruEntries 中查找 Entry

3. 检查 Entry 是否有效

4. 记录 READ 操作到 journal

5. 返回 Snapshot 对象

3.3、写入缓存

// 开始编辑
DiskLruCache.Editor editor = cache.edit(key);
if (editor != null) {
    try {
        // 获取输出流
        OutputStream outputStream = editor.newOutputStream(0);
        // 写入数据
        // ...
        // 提交编辑
        editor.commit();
    } catch (IOException e) {
        // 放弃编辑
        editor.abort();
    }
}

核心实现:

1. 检查 key 是否合法

2. 检查是否已存在编辑

3. 创建临时文件

4. 记录 DIRTY 操作到 journal

5. 提交时记录 CLEAN 操作

6. 更新 LRU 顺序和缓存大小

3.4、删除缓存

// 删除缓存
cache.remove(key);

核心实现:

1. 检查 key 是否合法

2. 从 lruEntries 中移除 Entry

3. 删除对应的缓存文件

4. 记录 REMOVE 操作到 journal

5. 更新缓存大小

3.5、清理缓存

// 清理超出大小的缓存
cache.trimToSize(maxSize);

核心实现:

1. 计算超出的大小

2. 按 LRU 顺序移除最旧的条目

3. 直到缓存大小符合要求

4、并发控制与事务

4.1、并发控制

DiskLruCache 使用以下机制保证并发安全:

- synchronized 锁:保护关键操作

- 原子操作:确保数据一致性

- 文件锁:防止多进程同时访问

- 编辑锁:确保同一键的编辑操作互斥

4.2、事务支持

DiskLruCache 通过以下步骤保证事务完整性:

1. 写入时创建临时文件

2. 提交时原子性地重命名文件

3. journal 日志记录所有操作

4. 恢复时通过 journal 重建状态

4.3、异常处理

- 写入失败时自动回滚

- 恢复时处理损坏的 journal

- 清理无效的缓存文件

- 确保缓存状态一致

5、源码优化技巧

5.1、性能优化

- 使用缓冲区减少 I/O 操作

- 延迟写入 journal,批量提交

- 使用 LinkedHashMap 实现 LRU 算法

- 定期清理过期缓存

5.2、内存优化

- 避免内存泄漏,及时关闭流

- 合理设置缓存大小

- 使用 SoftReference 管理缓存对象

- 避免一次性加载过多数据

5.3、安全性

- 验证 key 的合法性

- 防止缓存注入攻击

- 处理恶意文件

- 权限控制

6、使用示例与最佳实践

6.1、基本使用示例

// 初始化
File cacheDir = getCacheDir(context);
DiskLruCache cache = DiskLruCache.open(
    cacheDir,
    BuildConfig.VERSION_CODE,
    1,
    10 * 1024 * 1024 // 10MB 缓存
);

// 写入缓存
String key = generateKey(url);
DiskLruCache.Editor editor = cache.edit(key);
if (editor != null) {
    OutputStream os = editor.newOutputStream(0);
    // 下载并写入数据
    downloadToStream(url, os);
    editor.commit();
}

// 读取缓存
DiskLruCache.Snapshot snapshot = cache.get(key);
if (snapshot != null) {
    InputStream is = snapshot.getInputStream(0);
    // 使用缓存数据
    bitmap = BitmapFactory.decodeStream(is);
    snapshot.close();
}

// 关闭缓存
cache.close();

6.2、最佳实践

- 合理设置缓存大小(通常为 5-20MB)

- 使用固定长度的 key(如 MD5 哈希)

- 及时关闭 Snapshot 和 Editor

- 定期清理缓存

- 处理网络异常和缓存过期

6.3、注意事项

- 不要在主线程执行缓存操作

- 处理缓存文件损坏的情况

- 考虑应用版本升级时的缓存兼容性

- 监控缓存使用情况

6.4、总结

DiskLruCache 是一个设计精良的磁盘缓存实现,通过 LRU 算法和事务支持,提供了高效、可靠的缓存机制。它的源码结构清晰,实现了完整的缓存生命周期管理,是学习文件系统缓存的优秀范例。

关键要点:

        - 基于 LRU 算法自动管理缓存

        - 支持并发操作和事务

        - 提供简单易用的 API

        - 适用于各种缓存场景

掌握 DiskLruCache 的使用和原理,对于开发高性能、响应迅速的 Android 应用非常重要,是 Android 开发者必备的技能之一。

相关推荐

精选
Airbnb开源项目
开源

Airbnb开源项目

Airbnb Engineering & Data ScienceAirflow Use Apache Airflow (incubating) to author workflows as directed acyclic graphs (DAGs) of tasks12,263Airpal Web UI for PrestoDB2,502Aerosolve A machine learning

1.8k
OkHttp线程池和连接池
开源

OkHttp线程池和连接池

OkHttp线程池和连接池了解了OkHttp的网络请求流程以及拦截器实现原理,再关注OkHttp中两个重要的:OkHttp的线程池和连接池。1、OkHttp线程池在OkHttp网络请求流程一文中,我们分析了OkHttp异步和同步请求流程。请求最后都在Dispatcher中分发调度处理,最后被ExecutorService执行。1.1、DispatcherDispatcher中执行任务的执行器是ex

5.2k
优美的开源动效库:Lottie
开源

优美的开源动效库:Lottie

优美的开源动效库:Lottie1、强大的动效LottieLottie是一个适用于Android,iOS,Web和Windows的库,它可以使用Bodymovin解析以json格式导出的Adobe After Effects动画,并在移动设备和Web上原生渲染它们!GitHub:https://github.com/airbnb/lottie-androidLottie官网:http://airbn

4.3k