深入浅出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打造高效的磁盘缓存系统!

相关推荐
Kapaseker3 小时前
一杯美式搞懂 Any、Unit、Nothing
android·kotlin
黄林晴3 小时前
你的 Android App 还没接 AI?Gemini API 接入全攻略
android
恋猫de小郭13 小时前
2026 Flutter VS React Native ,同时在 AI 时代 VS Native 开发,你没见过的版本
android·前端·flutter
冬奇Lab14 小时前
PowerManagerService(上):电源状态与WakeLock管理
android·源码阅读
BoomHe19 小时前
Now in Android 架构模式全面分析
android·android jetpack
二流小码农1 天前
鸿蒙开发:上传一张参考图片便可实现页面功能
android·ios·harmonyos
鹏程十八少1 天前
4.Android 30分钟手写一个简单版shadow, 从零理解shadow插件化零反射插件化原理
android·前端·面试
Kapaseker1 天前
一杯美式搞定 Kotlin 空安全
android·kotlin
三少爷的鞋1 天前
Android 协程时代,Handler 应该退休了吗?
android
火柴就是我2 天前
让我们实现一个更好看的内部阴影按钮
android·flutter