DiskLruCache源码浅析
Github源码传送门:DiskLruCache
一、简单使用
|
|
二、打开缓存
DiskLruCache是不能new出实例的,如果我们要创建一个DiskLruCache的实例,则需要调用它的open()方法
|
|
缓存通常都会存放在/sdcard/Android/data/application package/cache 这个路径下面,但同时我们又需要考虑如果这个手机没有SD卡,或者SD正好被移除了的情况,就会被放在/data/data/application package/cache
DiskLruCache的构造方法并没有做别的事情,只是简单的将对应成员变量进行初始化,open()方法主要围绕着journal文件的创建与读写而展开的,如下所示:
- readJournal():读取journal文件,主要是读取文件头里的信息进行检验,然后调用readJournalLine()逐行去读取,根据读取的内容,执行相应的缓存 添加、移除等操作。
- rebuildJournal():重建journal文件,重建journal文件主要是写入文件头(上面提到的journal文件都有的前面五行的内容)。
- rocessJournal():计算当前缓存容量的大小。
三、日志文件journal
我们如果去打开缓存目录,就会发现除了缓存文件,还会发现一个journal文件,journal文件用来记录缓存的操作记录的,如下所示:
|
|
具体含义:
- 第一行:libcore.io.DiskLruCache,固定字符串。
- 第二行:1,DiskLruCache源码版本号。
- 第三行:1,App的版本号,通过open()方法传入进去的。
- 第四行:1,每个key对应几个缓存文件,一般为1.
- 第五行:空行
- 第六行及后续行:缓存操作记录。
第六行及后续行表示缓存操作记录,关于操作记录,我们需要了解以下三点:
- DIRTY 表示一个entry正在被写入。写入分两种情况,如果成功会紧接着写入一行CLEAN的记录;如果失败,会增加一行REMOVE记录。注意单独只有DIRTY状态的记录是非法的。
- 当手动调用remove(key)方法的时候也会写入一条REMOVE记录。
- READ就是说明有一次读取的记录。
- CLEAN的后面还记录了文件的长度,注意可能会一个key对应多个文件,那么就会有多个数字。
rebuildJournal()
就是重新构建Journal文件,没什么好说的。
继续看,readJounal()
|
|
该方法的逻辑就是根据记录中不同的操作类型进行相应的处理,如下所示:
- 如果该条记录以REMOVE为开头,则执行删除操作。
- 如果该key不存在,则新建Entry并加入lruEntries。
- 如果该条记录以CLEAN为开头,则初始化entry,并设置entry.readable为true、设置entry.currentEditor为null,初始化entry长度。
- 如果该条记录以DIRTY为开头。则设置currentEditor对象。
- 如果该条记录以READ为开头,则什么也不做。
说了这么多,readJournalLine()方法主要是通过读取journal文件的每一行,然后封装成entry对象,放到了LinkedHashMap集合中。并且根据每一行不同的开头,设置entry的值。也就是说通过读取这 个文件,我们把所有的在本地缓存的文件的key都保存到了集合中,这样我们用的时候就可以通过集合来操作了。
processJournal()
|
|
DIRTY 表示一个entry正在被写入。写入分两种情况,如果成功会紧接着写入一行CLEAN的记录;如果失败,会增加一行REMOVE记录。注意单独只有DIRTY状态的记录是非法的。
该方法主要用来计算当前的缓存总容量,并删除非法缓存记录以及该记录对应的文件。
四、写入缓存操作
DiskLruCache是通过edit来写入缓存的
|
|
主要做了两件事情:
- 从集合中找到对应的实例(如果没有创建一个放到集合中),然后创建一个editor,将editor和entry关联起来。
- 向journal中写入一行操作数据(DITTY 空格 和key拼接的文字),表示这个key当前正处于编辑状态。
然后将之前创建的outStream写入缓存
|
|
index表示之前一个key对应的缓存文件,index就是我们开始在open()方法里传入的valueCount,这个valueCount表示了一个key对应几个value,也就是说一个key对应几个缓存文件。那么现在传入的这个index就表示 要缓存的文件时对应的第几个value。
接着调用Editor的commit()方法就可以完成缓存文件的写入了
|
|
如果通过输出流写入缓存文件出错了就把集合中的缓存移除掉,如果没有错误就执行completeEdit(this, true)
DiskLruCache的completeEdit()方法来完成缓存写入
|
|
这个方法一共做了以下几件事情:
- 如果输出流写入数据成功,就把写入的临时文件重命名为正式的缓存文件
- 重新设置当前总缓存的大小
- 向journal文件写入一行CLEAN开头的字符(包括key和文件的大小,文件大小可能存在多个 使用空格分开的)
- 如果输出流写入失败,就删除掉写入的临时文件,并且把集合中的缓存也删除
- 向journal文件写入一行REMOVE开头的字符
- 重新比较当前缓存和最大缓存的大小,如果超过最大缓存或者journal文件的操作大于2000条,就把集合中的缓存删除一部分,直到小于最大缓存,重新建立新的journal文件
到这里,缓存的插入流程就完成了。
五、读取缓存
写入成功了,那么下面看一下读取缓存的流程。
读取缓存是由DiskLruCache的get()方法来完成的
|
|
读取操作主要完成了以下几件事情:
- 获取对应的entry。
- 打开所有缓存文件的输入流,等待被读取。
- 向journal写入一行READ开头的记录,表示执行了一次读取操作。
- 如果缓存总大小已经超过了设定的最大缓存大小或者操作次数超过了2000次,就开一个线程将集合中的数据删除到小于最大缓存大小为止并重新写journal文件。
- 返回一个缓存文件快照,包含缓存文件大小,输入流等信息。
六、删除缓存
删除缓存是由DiskLruCache的remove()方法来完成
|
|
删除操作主要做了以下几件事情:
- 获取对应的entry。
- 删除对应的缓存文件,并将缓存大小置为0.
- 向journal文件添加一行REMOVE开头的记录,表示执行了一次删除操作。
- 如果缓存总大小已经超过了设定的最大缓存大小或者操作次数超过了2000次,就开一个线程将集合中的数据删除到小于最大缓存大小为止并重新写journal文件。