統(tǒng)一接口interface DiskCache
緩存策略有
1.0.0版本出現(xiàn) UnlimitedDiskCache
1.3.1版本出現(xiàn) LimitedAgeDiskCache:
1.9.2版本出現(xiàn) LruDiskCache 在ext擴展包下面
其中UnlimitedDiskCache是默認的緩存模式
UnlimitedDiskCache和LimitedAgeDiskCache都是繼承自BaseDiskCache,而BaseDiskCache是一個抽象類,她實現(xiàn)了DiskCache接口,完成了保存,移除等基本操作
LruDiskCache是后來擴展的,直接實現(xiàn)了DiskCache
先說說保存文件名如何生成的吧!這個名字一定要是唯一的,不能重復!
算法相當重要,這里一共提供了MD5和HashCode
- HashCodeFileNameGenerator
- Md5FileNameGenerator
統(tǒng)一實現(xiàn)了FileNameGenerator接口
public class HashCodeFileNameGenerator implements FileNameGenerator {
@Override
public String generate(String imageUri) {
return String.valueOf(imageUri.hashCode());
}
}
直接取hashCode()
public class Md5FileNameGenerator implements FileNameGenerator {
private static final String HASH_ALGORITHM = "MD5";
private static final int RADIX = 10 + 26; // 10 digits + 26 letters
@Override
public String generate(String imageUri) {
byte[] md5 = getMD5(imageUri.getBytes());
BigInteger bi = new BigInteger(md5).abs();
return bi.toString(RADIX);
}
private byte[] getMD5(byte[] data) {
byte[] hash = null;
try {
MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM);
digest.update(data);
hash = digest.digest();
} catch (NoSuchAlgorithmException e) {
L.e(e);
}
return hash;
}
}
這里先是通過MD5做摘要算法,然后通過BigInteger做進制轉(zhuǎn)換,看著有點懵逼!我做個測試
如果我的地址是 http://www.reibang.com
new String(md5)結(jié)果是(亂碼) ???v?A?'[f??R?)?
bi.toString() 結(jié)果是 94697506358415651344405842152910083822
bi.toString(10 + 26) 結(jié)果是 47u6apm4arcy3jl456iglawa6
仔細想想,這里10 + 26代表0-9 A-Z 一共36個字符,平常用的最多的16進制,16進指最大是F代表15,這里為了降低文件名的長度,所以將進制用最大值,Z代表35, radix的范圍是MIN_RADIX~MAX_RADIX
MIN_RADIX = 2
MAX_RADIX = 36
這下明白了代碼用意
1.UnlimitedDiskCache
該模式,Cache緩存是無線增長的
代碼比較簡單,僅僅是3個構(gòu)造方法
public UnlimitedDiskCache(File cacheDir) {
super(cacheDir);
}
public UnlimitedDiskCache(File cacheDir, File reserveCacheDir) {
super(cacheDir, reserveCacheDir);
}
public UnlimitedDiskCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator) {
super(cacheDir, reserveCacheDir, fileNameGenerator);
}
第二個參數(shù)Reserve directory是儲備目錄,也就是主目錄不可用的情況下,會用這個目錄,心思極恐啊
2.LimitedAgeDiskCache
有期限的緩存,也就是當超過了自定義的時間時,緩存就刪除,這就想Cookie的MaxAge一樣,如果到期了Cookie就失效了!
核心代碼
private void rememberUsage(String imageUri) {
File file = getFile(imageUri);
long currentTime = System.currentTimeMillis();
file.setLastModified(currentTime);
loadingDates.put(file, currentTime);
}
每次保存一個bitmap,就把那個文件setLastModified修改最近一次的時間,然后把文件和時間緩存到內(nèi)存里,方便get取操作
好,接著看下取操作
@Override
public File get(String imageUri) {
File file = super.get(imageUri);
if (file != null && file.exists()) {
boolean cached;
Long loadingDate = loadingDates.get(file);
if (loadingDate == null) {
cached = false;
loadingDate = file.lastModified();
} else {
cached = true;
}
if (System.currentTimeMillis() - loadingDate > maxFileAge) {
file.delete();
loadingDates.remove(file);
} else if (!cached) {
loadingDates.put(file, loadingDate);
}
}
return file;
}
先讀文件,判斷文件是否存在,如果存在,從內(nèi)存里取出上次文件的修改時間,如果時間內(nèi)存中沒有,就直接讀File的屬性,然后通過與當前的時間做比對,如果過期了,就把文件刪掉,索引也從內(nèi)存中移除掉,否則有效,直接返回文件!
3.LruDiskCache
基于傳說中的"Least-Recently Used",也即是近期最少使用算法,它是一個適配器,它適配了另一個關(guān)鍵類DiskLruCache,這個是硬盤存儲的cache的類,它是square公司大神JakeWharton的一個項目
https://github.com/JakeWharton/DiskLruCache
DiskLruCache基于
LinkedHashMap<String, Entry> lruEntries = new LinkedHashMap<String, Entry>(0, 0.75f, true);
核心思想:
默認是按插入順序排序拳恋,如果指定訪問順序排序介杆,那么調(diào)用get方法后焊刹,會將這次訪問的元素移至鏈表尾部擎值,不斷訪問可以形成按訪問順序排序的鏈表晾浴。 可以重寫removeEldestEntry方法返回true值指定插入元素時移除最老的元素眉抬。
有一次看動腦學院的講打造牛逼的圖片緩存框架
也就就是針對這個集合做一些基礎(chǔ)操作,達到磁盤緩存...
DiskLruCache的詳細分析,網(wǎng)上有很多相關(guān)文章,我這里找了一篇
http://blog.csdn.net/guolin_blog/article/details/28863651
UIL中LruDiskCache適配了DiskLruCache,在構(gòu)造方法中初始化緩存DiskLruCache實例
cache = DiskLruCache.open(cacheDir, 1, 1, cacheMaxSize, cacheMaxFileCount);
cacheMaxSize如果不指定的話,就取Long.MAX_VALUE最大值0x7FFFFFFFFFFFFFFFL
通過DiskLruCache.Editor緩存文件
public boolean save(String imageUri, Bitmap bitmap) throws IOException {
DiskLruCache.Editor editor = cache.edit(getKey(imageUri));
if (editor == null) {
return false;
}
OutputStream os = new BufferedOutputStream(editor.newOutputStream(0), bufferSize);
boolean savedSuccessfully = false;
try {
savedSuccessfully = bitmap.compress(compressFormat, compressQuality, os);
} finally {
IoUtils.closeSilently(os);
}
if (savedSuccessfully) {
editor.commit();
} else {
editor.abort();
}
return savedSuccessfully;
}
有點像SharedPreference,緩存key是用文件名生成器生成的,默認bitmap壓縮格式是PNG,然后移除,讀取都是i依賴key
DiskLruCache有一個journal文件
每當我們調(diào)用一次DiskLruCache的edit()方法時儡嘶,都會向journal文件中寫入一條DIRTY記錄张峰,表示我們正準備寫入一條緩存數(shù)據(jù),但不知結(jié)果如何堪簿。然后痊乾,調(diào)用commit()方法表示寫入緩存成功,這時會向journal中寫入一條CLEAN記錄椭更,意味著這條“臟”數(shù)據(jù)被“洗干凈了”靴寂,調(diào)用abort()方法表示寫入緩存失敗,這時會向journal中寫入一條REMOVE記錄摆屯。
也就是說敌厘,每一行DIRTY的key,后面都應該有一行對應的CLEAN或者REMOVE的記錄舌狗,否則這條數(shù)據(jù)就是“臟”的叽奥,會被自動刪除掉。