程序猿免不了要和图片缓存打交道,最近也跟我杠上了。图片的缓存框架很多,大家所熟知的有Universal Image Loader,还有 volley ,最近在看蘑菇街TeamTalk源码的时候发现了,它用的是picasso。你无法说那个好,那个坏,除非你真的用过.我想他们的原理都一样,只是实现细节方面有些差异罢了!在我的应用里面选用了Universal Image Loader,这个开源框架下载地址如下:https://github.com/nostra13/Android-Universal-Image-Loader
Github上是这样描述这个框架的:UIL的目的是提供一个强大的,非常灵活且可自定义的图片下载缓存和显示的工具。它提供了许多配置选项方便更好的控制图片下载和缓存过程中的处理。
先来看一下UIL目录结构:好的开源框架从目录结构你就能看出它大体的功能模块。
Cache部分: 就是UIL的中心思想----缓存, 它又分为两部分:磁盘缓存和内存缓存。
Core部分: 显示控制核心部分,辅助cache部分完美实现UI显示,代码流程控制。
Utils部分: 这个我们每一个Android应用基本都有工具类
第一部分,Cache:
磁盘部分:
Disc文件夹下: 两个类DiscCacheAware和DiskCache, 主要定义一些文件缓存基本操作接口。包括保存,获取,关闭,清除等等。
Disc.impl文件夹下: 看名字能猜个大概没错实现类,BaseDiscCache还是一个抽象类,实现了上面文件缓存的基本接口。另外LimitedAgeDiscCache 和UnlimitedDiscCache 两个类,继承自BaseDiscCache 分别实现了,限制文件时间存活时间缓存和没有限制文件缓存。
UnlimitedDiscCache和BasediscCache 比较简单提供了文件两种保存save接口,获取文件get接口,remove删除缓存接口等等,就不错详细分析。
LimitedAgeDiscCache和上面两个不一样的是,在基本文件缓存的基础上,提供了一种限制文件最长可用存活时间的一个策略。看一下具体实现:
private final Map<File, Long>loadingDates = Collections.synchronizedMap(new HashMap<File, Long>());
第一个map 变量 loadingDates用来保存文件名和文件对应的修改时间。
public LimitedAgeDiscCache(FilecacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator, longmaxAge) {
super(cacheDir,reserveCacheDir, fileNameGenerator);
this.maxFileAge = maxAge *1000; // to milliseconds
}
构造函数:LimitedAgeDiscCache第一个参数是指定文件缓存目录,第二个参数是作为文件缓存目录的一个backup , 第三个参数是文件最长可用存活时间。
下面就是在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;
}
上面代码主要是从map loadingDates获取文件修改时间,如果为空直接通过file接口获取文件最新修改时间。
if(System.currentTimeMillis() - loadingDate > maxFileAge) {
file.delete();
loadingDates.remove(file);
} else if (!cached){
loadingDates.put(file,loadingDate);
}
}
上面代码主要是拿当前时间和文件最新修改时间比较,大于构造函数设定的maxFileAge 时间就删除文件,如果不大于,而且loadingDates没有该文件的缓存添加。
return file;
}
Disc.Impl.ext文件夹下: 这个文件夹下放的类文件是对磁盘缓存实现缓存文件LRU算法的一个扩展,类结构图如下:
其实就是LruDiscCache封装了DiscLruCache对文件缓存LRU的实现。DiscLruCache的实现我有一篇文章分析。http://blog.csdn.net/zhangyawen1i/article/details/40620787
Disc.Impl.nameIng文件下:主要是缓存文件名的生成方式,也可以自定义,就不做分析了。
接下来看看cache缓存的第二部分:memory
这部分代码一共有12个类,各种缓存策略,看上去好像挺复杂的,其实远比你想象的简单。下面是12类的类图
照着这个类图从上往下看一遍,这几个类的不同策略也不难分析,有兴趣的同学可以自己看看!
有意思的是,我看到一个鸡肋类WeakMemoryCache,为什么这么说了,这个类仅仅只是实现了,基类BaseMemoryCache的createReference函数,以弱引用的而形式返回。细心的你可能会发现,类图最底层的四个类createReference全是弱引用形式返回。视乎这个设计者的本意是希望LimitedMemoryCache 继承至WeakMemoryCache,然后再派生出最底下的四个类,但是LimitedMemoryCache因为已经实现了大小的限制,弱引用视乎又不是那么必须了,如果用户需要实现自己的LimitedMemoryCache 又希望是强引用,那就免不了要重新copy一份LimitedMemoryCache的code。也许出于这个考虑放弃了WeakMemoryCache。
另外,值得一说的是,LRULimitedMemoryCache利用的是LinkedHashMap 的功能来实现Lru队列。