着手阅读DiskLruCache 源码无可厚非先去了解 DiskLruCache 这个框架的用法 见下面代码:
File cacheDir = getDiskCacheDir(this, "cache");
if (!cacheDir.exists()) {
cacheDir.mkdirs();
}
mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(this), 1, 10 * 1024 * 1024);
代码中出现了 DiskLruCache open 方法:
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
throws IOException {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
if (valueCount <= 0) {
throw new IllegalArgumentException("valueCount <= 0");
}
// prefer to pick up where we left off
DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
if (cache.journalFile.exists()) {
try {
cache.readJournal();
cache.processJournal();
cache.journalWriter = new BufferedWriter(new FileWriter(cache.journalFile, true),
IO_BUFFER_SIZE);
return cache;
} catch (IOException journalIsCorrupt) {
// System.logW("DiskLruCache " + directory + " is corrupt: "
// + journalIsCorrupt.getMessage() + ", removing");
cache.delete();
}
}
// create a new empty cache
directory.mkdirs();
cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
cache.rebuildJournal();
return cache;
}
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
open 方法的三个参数 directory 代表缓存的文件夹,appVersion ,valueCount 缓存的文件数一般是1
journalFile 是一个日志文件记录了对文件的读写删除等操作记录,格式如下
libcore.io.DiskLruCache
1
100
2
CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054
DIRTY 335c4c6028171cfddfbaae1a9c313c52
CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342
REMOVE 335c4c6028171cfddfbaae1a9c313c52
DIRTY 1ab96a171faeeee38496d8b330771a7a
CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234
READ 335c4c6028171cfddfbaae1a9c313c52
READ 3400330d1dfc7f3f7f4b8d4d803dfcf6
DIRTY:标识 entry 即将被创建或者被更新 接下来执行 CLEAN 或者 REMOVE 操作
CLEAN:表示 缓存的entry 即成功发布或者即将被读取
READ:文件被读取
REMOVE:文件从缓存中被移除
代码中先创建一个journalFileTmp 临时文件,文件写入成功之后重命名为 journalFile
日志文件创建成功接下来就可以调用 save 缓存方法了
String key = hashKeyForDisk(imageUrl);
DiskLruCache.Editor editor = null;
editor = mDiskLruCache.edit(key);
if (editor != null) {
OutputStream outputStream = editor.newOutputStream(0);
if (downloadUrlToStream(imageUrl, outputStream)) {
editor.commit();
} else {
editor.abort();
}
}
//不该频繁的flush
mDiskLruCache.flush();
此段代码创建了 entry 第一次该缓存文件肯定是不存在的,所以edit 方法中肯定是创建了一个entry 以及对应的Editor 对象来操作entry 对象;在 DiskLruCache 框架中把每一个操作对象看成是一个entry 节点,包含了对应的key(url 访问地址的MD5的编码)以及length, 是否刻度readable, 还有一个Editor 对象用来操作 entry,
private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException {
checkNotClosed();
validateKey(key);
Entry entry = lruEntries.get(key);
if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER
&& (entry == null || entry.sequenceNumber != expectedSequenceNumber)) {
return null; // snapshot is stale
}
if (entry == null) {
entry = new Entry(key);
lruEntries.put(key, entry);
} else if (entry.currentEditor != null) {
return null; // another edit is in progress
}
Editor editor = new Editor(entry);
entry.currentEditor = editor;
// flush the journal before creating files to prevent file leaks
journalWriter.write(DIRTY + ' ' + key + '\n');
journalWriter.flush();
return editor;
}
既然上面的代码返回了一个Editor 对象,记下来我们就该保存了
private synchronized void completeEdit(Editor editor, boolean success) throws IOException {
Entry entry = editor.entry;
if (entry.currentEditor != editor) {
throw new IllegalStateException();
}
// if this edit is creating the entry for the first time, every index must have a value
if (success && !entry.readable) {
for (int i = 0; i < valueCount; i++) {
if (!entry.getDirtyFile(i).exists()) {
editor.abort();
throw new IllegalStateException("edit didn't create file " + i);
}
}
}
for (int i = 0; i < valueCount; i++) {
File dirty = entry.getDirtyFile(i);
if (success) {
if (dirty.exists()) {
File clean = entry.getCleanFile(i);
dirty.renameTo(clean);
long oldLength = entry.lengths[i];
long newLength = clean.length();
entry.lengths[i] = newLength;
size = size - oldLength + newLength;
}
} else {
deleteIfExists(dirty);
}
}
redundantOpCount++;
entry.currentEditor = null;
if (entry.readable | success) {
entry.readable = true;
journalWriter.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');
if (success) {
entry.sequenceNumber = nextSequenceNumber++;
}
} else {
lruEntries.remove(entry.key);
journalWriter.write(REMOVE + ' ' + entry.key + '\n');
}
if (size > maxSize || journalRebuildRequired()) {
executorService.submit(cleanupCallable);
}
}
上面是不是用到了journalWriter 进行了写入CLEAN 操作。既然是缓存作用,那么代码中的size 就是用来计算 缓存文件的大小了。
文中 用LinkedHashMap 来记录缓存文件 具体的可参见 大神的 彻头彻尾理解 LinkedHashMap https://blog.csdn.net/justloveyou_/article/details/71713781