分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow
也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!
转载请标明出处:
http://blog.csdn.net/lmj623565791/article/details/47251585;
本文出自:【张鸿洋的博客】
一、概述
依旧是整理东西,所以近期的博客涉及的东西可能会比较老一点,会分析一些经典的框架,我觉得可能也是每个优秀的开发者必须掌握的东西;那么对于Disk Cache,DiskLruCache可以算佼佼者了,所以我们就来分析下其源码实现。
对于该库的使用,推荐老郭的blog Android DiskLruCache完全解析,硬盘缓存的最佳方案
如果你不是很了解用法,那么注意下面的几点描述,不然直接看源码分析可能雨里雾里的。
- 首先,这个框架会涉及到一个文件,叫做journal,这个文件中会存储每次读取操作的记录;
对于获取一个DiskLruCache,是这样的:
DiskLruCache.open(directory, appVersion, valueCount, maxSize) ;
- 1
- 2
关于存一般是这么使用的:
String key = generateKey(url); DiskLruCache.Editor editor = mDiskLruCache.edit(key); OuputStream os = editor.newOutputStream(0);
- 1
- 2
- 3
因为每个实体都是个文件,所以你可以认为这个os指向一个文件的FileOutputStream,然后把你想存的东西写入就行了,写完以后记得调用:
editor.commit()
。关于取一般是这样的:
DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key); if (snapShot != null) { InputStream is = snapShot.getInputStream(0); }
- 1
- 2
- 3
- 4
还是那句,因为每个实体都是文件,所以你返回的is是个FileInputStream,你可以利用is读取出里面的内容,然后do what you want .
好了,关于Cache最主要就是存取了,了解这几点,就可以往下去看源码分析了。
还记得第一点说的journal文件么,首先就是它了。
二、journal文件
journal文件你打开以后呢,是这个格式;
libcore.io.DiskLruCache111DIRTY c3bac86f2e7a291a1a200b853835b664CLEAN c3bac86f2e7a291a1a200b853835b664 4698READ c3bac86f2e7a291a1a200b853835b664DIRTY c59f9eec4b616dc6682c7fa8bd1e061fCLEAN c59f9eec4b616dc6682c7fa8bd1e061f 4698READ c59f9eec4b616dc6682c7fa8bd1e061fDIRTY be8bdac81c12a08e15988555d85dfd2bCLEAN be8bdac81c12a08e15988555d85dfd2b 99READ be8bdac81c12a08e15988555d85dfd2bDIRTY 536788f4dbdffeecfbb8f350a941eea3REMOVE 536788f4dbdffeecfbb8f350a941eea3
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
首先看前五行:
- 第一行固定字符串
libcore.io.DiskLruCache
- 第二行DiskLruCache的版本号,源码中为常量1
- 第三行为你的app的版本号,当然这个是你自己传入指定的
- 第四行指每个key对应几个文件,一般为1
- 第五行,空行
ok,以上5行可以称为该文件的文件头,DiskLruCache初始化的时候,如果该文件存在需要校验该文件头。
接下来的行,可以认为是操作记录。
- DIRTY 表示一个entry正在被写入(其实就是把文件的OutputStream交给你了)。那么写入分两种情况,如果成功会紧接着写入一行CLEAN的记录;如果失败,会增加一行REMOVE记录。
- REMOVE除了上述的情况呢,当你自己手动调用remove(key)方法的时候也会写入一条REMOVE记录。
- READ就是说明有一次读取的记录。
- 每个CLEAN的后面还记录了文件的长度,注意可能会一个key对应多个文件,那么就会有多个数字(参照文件头第四行)。
从这里看出,只有CLEAN且没有REMOVE的记录,才是真正可用的Cache Entry记录。
分析完journal文件,首先看看DiskLruCache的创建的代码。
三、DiskLruCache#open
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize) throws IOException { // If a bkp file exists, use it instead. File backupFile = new File(directory, JOURNAL_FILE_BACKUP); if (backupFile.exists()) { File journalFile = new File(directory, JOURNAL_FILE); // If journal file also exists just delete backup file. if (journalFile.exists()) { backupFile.delete(); } else { renameTo(backupFile, journalFile, false); } } // 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(); return cache; } catch (IOException journalIsCorrupt) { System.out .println("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; }
- 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
首先检查存不存在journal.bkp(journal的备份文件)
如果存在:然后检查journal文件是否存在,如果正主在,bkp文件就可以删除了。
如果不存在,将bkp文件重命名为journal文件。
接下里判断journal文件是否存在:
如果不存在
创建directory;重新构造disklrucache;调用rebuildJournal建立journal文件
/*** Creates a new journal that omits redundant information. This replaces the* current journal if it exists.*/private synchronized void rebuildJournal() throws IOException { if (journalWriter != null) { journalWriter.close();}Writer writer = new BufferedWriter( new OutputStreamWriter(new FileOutputStream(journalFileTmp), Util.US_ASCII));try { writer.write(MAGIC); writer.write("\n"); writer.write(VERSION_1); writer.write("\n"); writer.write(Integer.toString(appVersion)); writer.write("\n"); writer.write(Integer.toString(valueCount)); writer.write("\n"); writer.write("\n"); for (Entry entry : lruEntries.values()) { if (entry.currentEditor != null) { writer.write(DIRTY + ' ' + entry.key + '\n'); } else { writer.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n'); } }} finally { writer.close();}if (journalFile.exists()) { renameTo(journalFile, journalFileBackup, true);}renameTo(journalFileTmp, journalFile, false);journalFileBackup.delete();journalWriter = new BufferedWriter( new OutputStreamWriter(new FileOutputStream(journalFile, true), Util.US_ASCII));}
- 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
可以看到首先构建一个journal.tmp文件,然后写入文件头(5行),然后遍历lruEntries(
lruEntries =
),当然我们这里没有任何数据。接下来将tmp文件重命名为journal文件。
new LinkedHashMap<String, Entry>(0, 0.75f, true);如果存在
如果已经存在,那么调用
readJournal
。private void readJournal() throws IOException {StrictLineReader reader = new StrictLineReader(new FileInputStream(journalFile), Util.US_ASCII);try { String magic = reader.readLine(); String version = reader.readLine(); String appVersionString = reader.readLine(); String valueCountString = reader.readLine(); String blank = reader.readLine(); if (!MAGIC.equals(magic) || !VERSION_1.equals(version) || !Integer.toString(appVersion).equals(appVersionString) || !Integer.toString(valueCount).equals(valueCountString) || !"".equals(blank)) { throw new IOException("unexpected journal header: [" + magic + ", " + version + ", " + valueCountString + ", " + blank + "]"); } int lineCount = 0; while (true) { try { readJournalLine(reader.readLine()); lineCount++; } catch (EOFException endOfJournal) { break; } } redundantOpCount = lineCount - lruEntries.size(); // If we ended on a truncated line, rebuild the journal before appending to it. if (reader.hasUnterminatedLine()) { rebuildJournal(); } else { journalWriter = new BufferedWriter(new OutputStreamWriter( new FileOutputStream(journalFile, true), Util.US_ASCII)); }} finally { Util.closeQuietly(reader);}}