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

相关推荐
志存高远663 小时前
kotlin 扩展函数
android·开发语言·kotlin
小镇敲码人5 小时前
【深入浅出MySQL】之数据类型介绍
android·数据库·mysql
柯南二号5 小时前
Android 实现一个隐私弹窗
android
UzumakiHan8 小时前
flutter权限允许访问
android·flutter
wangz7610 小时前
kotlin、jetpack compose、Android加速度传感器调用
android·kotlin·jetpack compose·加速度传感器
东坡大表哥10 小时前
【Android】Android签名解析
android·java
每次的天空10 小时前
Android学习总结之GetX库篇(场景运用)
android·javascript·学习
Ya-Jun12 小时前
性能优化实践:渲染性能优化
android·flutter·ios·性能优化
Hzhile12 小时前
攻防世界-php伪协议和文件包含
android·开发语言·安全·web安全·网络安全·php
追随远方14 小时前
详解 FFMPEG 交叉编译 `FLAGS` 和 `INCLUDES` 的作用
android·ffmpeg·myeclipse·音频编解码