统一接口interface DiskCache
缓存策略有
1.0.0版本出现 UnlimitedDiskCache
1.3.1版本出现 LimitedAgeDiskCache:
1.9.2版本出现 LruDiskCache 在ext扩展包下面
其中UnlimitedDiskCache是默认的缓存模式
UnlimitedDiskCache和LimitedAgeDiskCache都是继承自BaseDiskCache,而BaseDiskCache是一个抽象类,她实现了DiskCache接口,完成了保存,移除等基本操作
LruDiskCache是后来扩展的,直接实现了DiskCache
先说说保存文件名如何生成的吧!这个名字一定要是唯一的,不能重复!
算法相当重要,这里一共提供了MD5和HashCode
- HashCodeFileNameGenerator
- Md5FileNameGenerator
统一实现了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做进制转换,看着有点懵逼!我做个测试
如果我的地址是 http://www.jianshu.com
new String(md5)结果是(乱码) ���v�A�'[f��R�)�
bi.toString() 结果是 94697506358415651344405842152910083822
bi.toString(10 + 26) 结果是 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个构造方法
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);
}
第二个参数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修改最近一次的时间,然后把文件和时间缓存到内存里,方便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;
}
先读文件,判断文件是否存在,如果存在,从内存里取出上次文件的修改时间,如果时间内存中没有,就直接读File的属性,然后通过与当前的时间做比对,如果过期了,就把文件删掉,索引也从内存中移除掉,否则有效,直接返回文件!
3.LruDiskCache
基于传说中的"Least-Recently Used",也即是近期最少使用算法,它是一个适配器,它适配了另一个关键类DiskLruCache,这个是硬盘存储的cache的类,它是square公司大神JakeWharton的一个项目
https://github.com/JakeWharton/DiskLruCache
DiskLruCache基于
LinkedHashMap
核心思想:
默认是按插入顺序排序,如果指定访问顺序排序,那么调用get方法后,会将这次访问的元素移至链表尾部,不断访问可以形成按访问顺序排序的链表。 可以重写removeEldestEntry方法返回true值指定插入元素时移除最老的元素。
有一次看动脑学院的讲打造牛逼的图片缓存框架
也就就是针对这个集合做一些基础操作,达到磁盘缓存...
DiskLruCache的详细分析,网上有很多相关文章,我这里找了一篇
http://blog.csdn.net/guolin_blog/article/details/28863651
UIL中LruDiskCache适配了DiskLruCache,在构造方法中初始化缓存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文件
每当我们调用一次DiskLruCache的edit()方法时,都会向journal文件中写入一条DIRTY记录,表示我们正准备写入一条缓存数据,但不知结果如何。然后,调用commit()方法表示写入缓存成功,这时会向journal中写入一条CLEAN记录,意味着这条“脏”数据被“洗干净了”,调用abort()方法表示写入缓存失败,这时会向journal中写入一条REMOVE记录。
也就是说,每一行DIRTY的key,后面都应该有一行对应的CLEAN或者REMOVE的记录,否则这条数据就是“脏”的,会被自动删除掉。