深入浅出DiskLruCache原理

深入浅出DiskLruCache原理

一、DiskLruCache是什么?------手机里的文件仓库管理员

想象DiskLruCache就像是你手机里的一个智能仓库管理员:

  • 专门职责:管理图片等文件在手机存储中的缓存
  • 工作原则:最近最少使用的文件优先淘汰(LRU算法)
  • 存储位置:/data/data/<包名>/cache 或 SD卡缓存目录

它不同于内存缓存(速度极快但容量小),专门管理磁盘上的缓存文件,是图片三级缓存中的重要一环。

二、核心工作原理------仓库管理员的日常工作

1. 仓库结构设计

每个DiskLruCache仓库由以下部分组成:

erlang 复制代码
cache_dir/
  ├── journal      ← 仓库的账本文件(最重要!)
  ├── file.0       ← 实际缓存文件
  ├── file.1
  └── ...

2. 关键工作流程

存文件(put):
java 复制代码
// 就像仓库管理员收到新货
DiskLruCache.Editor editor = cache.edit("image1"); // 申请入库单
editor.newOutputStream(0).write(imageData);      // 把货物放进临时位置
editor.commit();                                 // 正式入库记账
取文件(get):
java 复制代码
// 就像有人来取货
DiskLruCache.Snapshot snapshot = cache.get("image1"); // 查账本
if (snapshot != null) {
    InputStream inputStream = snapshot.getInputStream(0); // 打开货架
    Bitmap bitmap = BitmapFactory.decodeStream(inputStream); // 取出货物
    snapshot.close(); // 关好货架
}
清理仓库(trimToSize):
java 复制代码
// 定期盘点,清理最久未使用的货物
cache.flush();      // 先确保账本最新
cache.trimToSize(); // 执行清理(默认在put/get时自动触发)

三、核心文件解析------账本(journal)的秘密

journal文件是DiskLruCache的核心,记录所有操作日志,格式如下:

lua 复制代码
libcore.io.DiskLruCache     ← 魔数(标识文件类型)
1                          ← 版本号
100                        ← APP版本号
2                          ← 每个key对应value数
                           ← 空行
CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054
DIRTY 335c4c6028171cfddfbaae1a9c313c52
REMOVE 335c4c6028171cfddfbaae1a9c313c52
READ 335c4c6028171cfddfbaae1a9c313c52

行类型说明:

  • DIRTY:表示正在写入(此时文件可能不完整)
  • CLEAN:表示成功写入(后面跟着文件大小)
  • REMOVE:表示已删除
  • READ:表示访问记录(用于LRU判断)

四、LRU淘汰算法------仓库的清理规则

DiskLruCache的清理策略就像仓库管理员的淘汰规则:

  1. 每次访问文件都会在journal中记录READ
  2. 当缓存大小超过限制时:
    • 从LinkedHashMap尾部开始检查(最久未使用)
    • 删除REMOVE标记的文件
    • 直到缓存大小低于限制
java 复制代码
// 简化版LRU实现逻辑
while (size > maxSize) {
    Entry toEvict = lruEntries.values().iterator().next();
    for (File file : toEvict.cleanFiles) {
        size -= file.length();
        file.delete(); // 删除实际文件
    }
    lruEntries.remove(toEvict.key); // 从记录中移除
}

五、关键特性解析

1. 文件命名机制

  • 原始key会经过MD5处理(避免特殊字符问题)
  • 每个key对应多个文件(通常图片缓存用1个)

2. 线程安全设计

  • 通过synchronized保证操作原子性
  • 所有文件操作必须通过Journal记录

3. 崩溃恢复

  • 启动时会读取journal文件重建内存状态
  • DIRTY状态但没有CLEAN记录的文件会被删除

4. 版本兼容

  • 头部的APP版本号变化时会清空缓存
java 复制代码
// 初始化时检查版本
if (journalReader.readInt() != appVersion) {
    deleteAll(); // 版本不同则清理
}

六、使用示例------完整缓存图片流程

1. 初始化缓存

java 复制代码
File cacheDir = getDiskCacheDir(context, "image_cache");
int cacheSize = 50 * 1024 * 1024; // 50MB
DiskLruCache cache = DiskLruCache.open(cacheDir, 1, 1, cacheSize);

2. 存储图片

java 复制代码
String imageUrl = "http://example.com/image.jpg";
String key = generateKey(imageUrl); // 通常用URL的MD5

DiskLruCache.Editor editor = cache.edit(key);
try (OutputStream out = editor.newOutputStream(0)) {
    bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
    editor.commit();
} catch (IOException e) {
    editor.abort();
}

3. 读取图片

java 复制代码
DiskLruCache.Snapshot snapshot = cache.get(key);
if (snapshot != null) {
    InputStream in = snapshot.getInputStream(0);
    Bitmap bitmap = BitmapFactory.decodeStream(in);
    snapshot.close();
    return bitmap;
}

4. 清理缓存

java 复制代码
// 手动触发清理
cache.flush(); // 确保journal更新
cache.trimToSize();

// 或者删除全部
cache.delete();

七、优化技巧

1. 合理设置缓存大小

  • 建议值:10MB-100MB
  • 计算公式:
java 复制代码
// 可用空间的1/8,但不超过50MB
long available = getUsableSpace(cacheDir);
long cacheSize = Math.min(available / 8, 50 * 1024 * 1024);

2. 定期维护

java 复制代码
// 每周清理一次
if (System.currentTimeMillis() > lastCacheCheck + WEEK_IN_MILLIS) {
    cache.trimToSize();
    lastCacheCheck = System.currentTimeMillis();
}

3. 异常处理

java 复制代码
try {
    // 缓存操作
} catch (IOException e) {
    cache.delete(); // 严重错误时重建缓存
}

八、常见问题解答

1. 为什么我的缓存文件变少了?

  • 可能触发了LRU淘汰
  • 可能APP版本升级导致清空
  • 可能用户手动清理了缓存

2. journal文件可以删除吗?

  • 不可以!除非同时删除所有缓存文件
  • journal损坏会导致缓存不可用

3. 如何查看缓存内容?

java 复制代码
for (DiskLruCache.Entry entry : cache.snapshot().values()) {
    Log.d("Cache", "Key: " + entry.getKey() + ", Size: " + entry.getLength());
}

九、总结

DiskLruCache就像个精明的仓库管理员:

  1. 严格记账:journal文件记录所有操作
  2. 智能清理:LRU算法自动淘汰旧文件
  3. 安全可靠:崩溃后能恢复一致状态

关键设计要点:

  • 文件+索引分离:实际文件与journal配合
  • LRU实现:通过LinkedHashMap+访问记录
  • 原子操作:通过DIRTY/CLEAN状态机制

使用记住三点:

  1. 初始化要设置合理大小
  2. 操作后及时flush()
  3. 定期维护trimToSize()

掌握DiskLruCache原理,你就能为APP打造高效的磁盘缓存系统!

相关推荐
移动开发者1号1 小时前
扫一扫的时候会经历哪些事
android·kotlin
移动开发者1号1 小时前
Fragment事务commit与commitNow区别
android·java
移动开发者1号1 小时前
动态加载组件原理详解
android·kotlin
移动开发者1号1 小时前
Android任务栈管理策略总结
android·kotlin
墨狂之逸才3 小时前
kotlin泛型实化
android·kotlin
_一条咸鱼_3 小时前
Android Runtime虚拟机实例创建与全局状态初始化(11)
android·面试·android jetpack
墨狂之逸才3 小时前
kotlin中:: 是一个非常重要的操作符,称为引用操作符
android·kotlin
工业互联网专业4 小时前
基于Android的记录生活APP_springboot+vue
android·vue.js·spring boot·毕业设计·源码·课程设计·记录生活app
小陶来咯4 小时前
【仿muduo库实现并发服务器】实现时间轮定时器
android·运维·服务器
Yeah_0day5 小时前
移动安全Android——客户端数据安全
android·客户端数据安全·本地文件权限配置·本地文件内容安全·本地日志内容安全