图片加载UIL的Disk缓存策略

统一接口interface DiskCache
缓存策略有
1.0.0版本出现 UnlimitedDiskCache
1.3.1版本出现 LimitedAgeDiskCache:
1.9.2版本出现 LruDiskCache 在ext扩展包下面
其中UnlimitedDiskCache是默认的缓存模式
UnlimitedDiskCacheLimitedAgeDiskCache都是继承自BaseDiskCache,而BaseDiskCache是一个抽象类,她实现了DiskCache接口,完成了保存,移除等基本操作
LruDiskCache是后来扩展的,直接实现了DiskCache

先说说保存文件名如何生成的吧!这个名字一定要是唯一的,不能重复!
算法相当重要,这里一共提供了MD5和HashCode

  1. HashCodeFileNameGenerator
  2. 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 lruEntries = new LinkedHashMap(0, 0.75f, true);
核心思想:

默认是按插入顺序排序,如果指定访问顺序排序,那么调用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的记录,否则这条数据就是“脏”的,会被自动删除掉。

你可能感兴趣的:(图片加载UIL的Disk缓存策略)