由JakeWharton大神所编写的DiskLruCache工程, 里面除去注释量,代码差不多500行,短短500行的代码,却拿到了3000多个star,以及800多个fork, 可见其代码设计的优越性,稳定性.并且得到了Google的推荐,以及众多明星公司的青睐.
抱着学习的态度去看了看代码,加上网上的介绍,学习了其原理.
首先,DiskLruCache原理很简单,就是将你的东西缓存到磁盘上,你可能说,我们也能啊.任何东西都可以转换成字节流的形式再转换文件形式存放在磁盘上,毕竟,所有东西都是二进制.
所以说,大家都能干的事情,但是干好可是很难的。而JakeWharton短短500行代码帮你干好了这件事情是很神奇的.
DiskLruCache只有3个类.
我们先看一下如何使用.以及使用后的效果.
1 2 3 4 5 6 7
| *cacheFile 缓存文件的存储路径 *appVersion 应用版本号。DiskLruCache 认为应用版本更新后所有的数据都因该从服务器重新拉取,因此需要版本号进行判断 *1 每条缓存条目对应的值的个数,这里设置为1个。 *10*1024*1014 缓存的最大空间10M **/ DiskLruCache mDiskLruCache = DiskLruCache.open(cacheFile, appVersion, 1, 10*1024*1014);
|
1 2 3 4 5
| DiskLruCache.Editor edit = mDiskLruCache.edit("xiamin"); OutputStream os = edit.newOutputStream(0); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os)); bw.write("test1"); edit.commit();
|
xiamin@xiamin:~/AndroidStudioProjects/KeepGank$ adb shell
shell@A37:/ cd /sdcard/Android/data/com.jerey.keepgank/cache/xiamin
shell@A37:/sdcard/Android/data/com.jerey.keepgank/cache/xiamin $ ls
32190eac33f22c640d3344af0bc3cf20.0 journal
shell@A37:/sdcard/Android/data/com.jerey.keepgank/cache/xiamin cat 32190eac33f22c640d3344af0bc3cf20.0
test1
shell@A37:/sdcard/Android/data/com.jerey.keepgank/cache/xiamin
shell@A37:/sdcard/Android/data/com.jerey.keepgank/cache/xiamin cat journal
libcore.io.DiskLruCache 1 1 1
DIRTY 32190eac33f22c640d3344af0bc3cf20
CLEAN 32190eac33f22c640d3344af0bc3cf20 0
shell@A37:/sdcard/Android/data/com.jerey.keepgank/cache/xiamin $
我们通过Adb shell进入手机查看,的确查看到了在/sdcard/Android/data/com.jerey.keepgank/cache/xiamin 目录下,存在journal文件与32190eac33f22c640d3344af0bc3cf20.0 文件,经过cat该文件,的确看到了我们写入的test1.
此时,已经证实了我们上面说的,以文件的形式存储数据。现在该我们一窥他是怎么干的了。
重要方法
DiskLruCache.open
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| 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"); } File backupFile = new File(directory, JOURNAL_FILE_BACKUP); if (backupFile.exists()) { log("backupFile.exists() 存在"); File journalFile = new File(directory, JOURNAL_FILE); if (journalFile.exists()) { backupFile.delete(); } else { log("不存在,将bkp文件重命名为journal文件"); renameTo(backupFile, journalFile, false); } } DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize); if (cache.journalFile.exists()) { log("journal文件是存在"); try { cache.readJournal(); cache.processJournal(); return cache; } catch (IOException journalIsCorrupt) { System.out .println("DiskLruCache " + directory + " is corrupt: " + journalIsCorrupt.getMessage() + ", removing"); cache.delete(); } } log("journal文件是不存在 新构造disklrucache;调用rebuildJournal建立journal文件"); directory.mkdirs(); cache = new DiskLruCache(directory, appVersion, valueCount, maxSize); cache.rebuildJournal(); return cache; }
|
mDiskLruCache.edit(“xiamin”);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 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; } if (entry == null) { entry = new Entry(key); lruEntries.put(key, entry); } else if (entry.currentEditor != null) { return null; } Editor editor = new Editor(entry); entry.currentEditor = editor; journalWriter.write(DIRTY + ' ' + key + '\n'); journalWriter.flush(); return editor; }
|
mDiskLruCache.get(key);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| public synchronized Snapshot get(String key) throws IOException { checkNotClosed(); validateKey(key); Entry entry = lruEntries.get(key); if (entry == null) { return null; } if (!entry.readable) { return null; } InputStream[] ins = new InputStream[valueCount]; try { for (int i = 0; i < valueCount; i++) { ins[i] = new FileInputStream(entry.getCleanFile(i)); } } catch (FileNotFoundException e) { for (int i = 0; i < valueCount; i++) { if (ins[i] != null) { Util.closeQuietly(ins[i]); } else { break; } } return null; } redundantOpCount++; journalWriter.append(READ + ' ' + key + '\n'); if (journalRebuildRequired()) { executorService.submit(cleanupCallable); } return new Snapshot(key, entry.sequenceNumber, ins, entry.lengths); }
|
结语
DiskLruCache其实封装了一个Editor和一个Snapshot帮我们写和读文件,内部构建一个靠谱的稳定的读写框架。
虽然事情简单,但逻辑还是很复杂的。源码可以下载下来看看。
我复制过来加了注释的,里面加了一些Debug的log和注释。有兴趣的可以看看。
https://github.com/Jerey-Jobs/KeepGank/tree/master/lrucache/src/main/java/com/jerey/lruCache
谢谢大家阅读,如有帮助,来个喜欢或者关注吧!
本文作者:Anderson/Jerey_Jobs
博客地址 : 夏敏的博客/Anderson大码渣/Jerey_Jobs
简书地址 : Anderson大码渣
CSDN地址 : Jerey_Jobs的专栏
github地址 : Jerey_Jobs