DiskLruCache是谷歌推荐的用来实现硬盘缓存的类,今天我们开始对于DiskLruCache的学习。DiskLruCache的测试代码:DiskLruCache的测试代码下载。关于FidkLruCache的使用,请参见我的博客:android基础---->LruCache的使用及原理

目录导航

  1. DiskLruCache缓存的代码实例
  2. DiskLruCache的原理分析
  3. 友情链接

DiskLruCache缓存的代码实例

我们通过一个案例来体会DiskLruCache的使用及执行的流程,在我们的项目中包含DiskLruCache文件:http://pan.baidu.com/s/1slR6pg5。项目结构如下:

aaarticlea/png;base64,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" alt="" />

一、 DiskLruCache的是一个final类,不能继承。如果我们要创建一个DiskLruCache的实例,则需要调用它的open()方法,如下:

public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)  

在AndroidManifest.xml加入网络权限和sd卡写入权限:

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

项目中创建并打开缓存:

try {
File cacheDir = getDiskCacheDir(this, "bitmap");
if (!cacheDir.exists()) {
cacheDir.mkdirs();
}
mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(this), 1, 10 * 1024 * 1024);
} catch (Exception e) {
e.printStackTrace();
}

二、 从网络得到图片资源,并写入缓存:

private void saveCache() {
new Thread(new Runnable() {
@Override
public void run() {
try {
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();
handler.post(new Runnable() {
@Override
public void run() {
textView.setText("saveCache done,the bitmap is ready");
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}

downloadUrlToStream方法用于从网络上获取图片资源:

private boolean downloadUrlToStream(String urlString, OutputStream outputStream) {
HttpURLConnection urlConnection = null;
BufferedOutputStream out = null;
BufferedInputStream in = null;
try {
final URL url = new URL(urlString);
urlConnection = (HttpURLConnection) url.openConnection();
in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);
out = new BufferedOutputStream(outputStream, 8 * 1024);
int b;
while ((b = in.read()) != -1) {
out.write(b);
}
return true;
} catch (final IOException e) {
e.printStackTrace();
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (final IOException e) {
e.printStackTrace();
}
}
return false;
}

三、 由于涉及到key的因素,我们写一个MD5对key进行编码:

public String hashKeyForDisk(String key) {
String cacheKey;
try {
final MessageDigest mDigest = MessageDigest.getInstance("MD5");
mDigest.update(key.getBytes());
cacheKey = bytesToHexString(mDigest.digest());
} catch (NoSuchAlgorithmException e) {
cacheKey = String.valueOf(key.hashCode());
}
return cacheKey;
} private String bytesToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(0xFF & bytes[i]);
if (hex.length() == 1) {
sb.append('0');
}
sb.append(hex);
}
return sb.toString();
}

四、 从缓存中读取数据:

private void readCache() {
//读取缓存
try {
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(hashKeyForDisk(imageUrl));
if (snapshot != null) {
InputStream is = snapshot.getInputStream(0);
Bitmap bitmap = BitmapFactory.decodeStream(is);
imageView.setImageBitmap(bitmap);
} else {
imageView.setImageBitmap(null);
}
} catch (IOException e) {
e.printStackTrace();
}
}

五、 移除缓存:

private void removeCache() {
try {
mDiskLruCache.remove(hashKeyForDisk(imageUrl));
} catch (IOException e) {
e.printStackTrace();
}
}

六、 清空缓存:

private void deleteCache() {
try {
//delete()方法内部会调用close()
mDiskLruCache.delete();
} catch (IOException e) {
e.printStackTrace();
}
}

七、 得到缓存的大小:

private void getCacheSize() {
textView.setText("cache size : " + mDiskLruCache.size() + "B");
}

八、 在onDesctory方法中关闭缓存:

@Override
protected void onDestroy() {
super.onDestroy();
try {
//关闭DiskLruCache,与open对应
mDiskLruCache.close();
} catch (IOException e) {
e.printStackTrace();
}
}

DiskLruCache的原理分析

经过上述实例的说明,我们对DiskLruCache的使用已经有了一些认识。现在我们开始DiskLruCache的原理分析:

创建打开缓存

一、 当创建打开缓存:mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(this), 1, 10 * 1024 * 1024);

创建日志journal文件,并且初始化journalWriter:

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) {
cache.delete();
}
} // create a new empty cache
directory.mkdirs();
cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
cache.rebuildJournal();
return cache;
}

在DiskLruCache的构造方法中,创建journal和journal.tmp文件。

private DiskLruCache(File directory, int appVersion, int valueCount, long maxSize) {
this.directory = directory;
this.appVersion = appVersion;
this.journalFile = new File(directory, JOURNAL_FILE);
this.journalFileTmp = new File(directory, JOURNAL_FILE_TMP);
this.valueCount = valueCount;
this.maxSize = maxSize;
}

在rebuildJournal方法中,在journalFileTmp临时文件中,写入一些数据,最后重命名为journalFile:

private synchronized void rebuildJournal() throws IOException {
if (journalWriter != null) {
journalWriter.close();
} Writer writer = new BufferedWriter(new FileWriter(journalFileTmp), IO_BUFFER_SIZE);
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');
}
} writer.close();
journalFileTmp.renameTo(journalFile);
journalWriter = new BufferedWriter(new FileWriter(journalFile, true), IO_BUFFER_SIZE);
}

二、 创建完成之后 ,在sd中存在journal文件:内容如下:

libcore.io.DiskLruCache  // MAGIC
1 // VERSION_1
1 // appVersion
1 // valueCount

写入缓存

二、 然后是这个代码:DiskLruCache.Editor editor  = mDiskLruCache.edit(key);其中的lruEntries是一个LinkedHashMap,用于记录资源的key与value。

当第一次edit时,lruEntries.get(key)返回的是空。这里会创建一个Entry对象,并在日志文件中写入DIRTY + ' ' + key + '\n'内容。最后返回包含这个entry的Editor。

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;
}

三、 OutputStream outputStream = editor.newOutputStream(0);downloadUrlToStream(imageUrl, outputStream);得到文件输出流,将从网络上请求到的资源写入到文件中。

这里关注下outputStream,它是由newOutputStream方法得到的:

public OutputStream newOutputStream(int index) throws IOException {
synchronized (DiskLruCache.this) {
if (entry.currentEditor != this) {
throw new IllegalStateException();
}
return new FaultHidingOutputStream(new FileOutputStream(entry.getDirtyFile(index)));
}
}

getDirtyFile文件是一个临时文件,也就是网络文件写入到这个临时文件当中:

public File getDirtyFile(int i) {
return new File(directory, key + "." + i + ".tmp");
}

四、 接着执行到了editor.commit()方法,如果没有出现错误的话:
创建一个key + "." + i的文件,将上述的dirty文件重命名为key + "." + i的文件。更新已经缓存的大小,并且删除上述的dirty文件。

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);
}
}

五、 当执行完写入之后,日志文件如下内容:abb7d9d7add6b9fba5314aec6e60c9e6就是上述MD5生成的key,15417是代表缓存的大小。并生成一个abb7d9d7add6b9fba5314aec6e60c9e6.0文件.

libcore.io.DiskLruCache
1
1
1 DIRTY abb7d9d7add6b9fba5314aec6e60c9e6
CLEAN abb7d9d7add6b9fba5314aec6e60c9e6 15417

从缓存中读取

一、 mDiskLruCache.get(key,以下是关键代码,中间省略了代码);

Entry entry = lruEntries.get(key); // 根据key从map中得到相应的value
  ....
InputStream[] ins = new InputStream[valueCount];
try {
for (int i = 0; i < valueCount; i++) {
ins[i] = new FileInputStream(entry.getCleanFile(i)); // 得到clearn文件输入流
}
} catch (FileNotFoundException e) {
// a file must have been deleted manually!
return null;
}
journalWriter.append(READ + ' ' + key + '\n'); // 在日志文件中写入内容
  ....
return new Snapshot(key, entry.sequenceNumber, ins); // 返回一个Snapshot对象

二、 snapshot.getInputStream(0)得到clean文件的输入流,然后通过Bitmap bitmap = BitmapFactory.decodeStream(is);方法得到缓存在硬盘的图片。

友情链接

04-16 02:36