标签:
讲到图片请求,主要涉及到网络请求,内存缓存,硬盘缓存等原理和4大引用的问题,概括起来主要有以下几个内容:
原理示意图
主体有三个,分别是UI,缓存模块和数据源(网络)。它们之间的关系如下:
① UI:请求数据,使用唯一的Key值索引Memory Cache中的Bitmap。
② 内存缓存:缓存搜索,如果能找到Key值对应的Bitmap,则返回数据。否则执行第三步。
③ 硬盘存储:使用唯一Key值对应的文件名,检索SDCard上的文件。
④ 如果有对应文件,使用BitmapFactory.decode*方法,解码Bitmap并返回数据,同时将数据写入缓存。如果没有对应文件,执行第五步。
⑤ 下载图片:启动异步线程,从数据源下载数据(Web)。
⑥ 若下载成功,将数据同时写入硬盘和缓存,并将Bitmap显示在UI中。
UIL中的内存缓存策略
1. 只使用的是强引用缓存
LruMemoryCache(这个类就是这个开源框架默认的内存缓存类,缓存的是bitmap的强引用,下面我会从源码上面分析这个类)2.使用强引用和弱引用相结合的缓存有
UsingFreqLimitedMemoryCache(如果缓存的图片总量超过限定值,先删除使用频率最小的bitmap)
LRULimitedMemoryCache(这个也是使用的lru算法,和LruMemoryCache不同的是,他缓存的是bitmap的弱引用) FIFOLimitedMemoryCache(先进先出的缓存策略,当超过设定值,先删除最先加入缓存的bitmap) LargestLimitedMemoryCache(当超过缓存限定值,先删除最大的bitmap对象) LimitedAgeMemoryCache(当 bitmap加入缓存中的时间超过我们设定的值,将其删除)3.只使用弱引用缓存
WeakMemoryCache(这个类缓存bitmap的总大小没有限制,唯一不足的地方就是不稳定,缓存的图片容易被回收掉)
我们直接选择UIL中的默认配置缓存策略进行分析。
1.
ImageLoaderConfiguration
config = ImageLoaderConfiguration.createDefault(context);
ImageLoaderConfiguration.createDefault(…)这个方法最后是调用Builder.build()方法创建默认的配置参数的。默认的内存缓存实现是LruMemoryCache,磁盘缓存是UnlimitedDiscCache。
所以android在以后的版本中建议使用LruMemoryCache进行管理。
LruMemoryCache:一种使用强引用来保存有数量限制的Bitmap的cache(在空间有限的情况,保留最近使用过的Bitmap)。每次Bitmap被访问时,它就被移动到一个队列的头部。当Bitmap被添加到一个空间已满的cache时,在队列末尾的Bitmap会被挤出去并变成适合被GC回收的状态。
注意:这个cache只使用强引用来保存Bitmap。
LruMemoryCache实现MemoryCache,而MemoryCache继承自MemoryCacheAware。
1.
public
interface
MemoryCache
extends
MemoryCacheAware<String,
Bitmap>
下面给出继承关系图
通过跟踪LruMemoryCache.get()的代码我们发现,LruMemoryCache声称保留在空间有限的情况下保留最近使用过的Bitmap,这是一个LinkedHashMap<String, Bitmap>,Icache的源码缓存的代码如下:
@Override
public ICache<String, Bitmap> getBitmapCache() {
if (bitmapCache == null) {
bitmapCache = new ICache<String, Bitmap>() {
@Override
public void remove(String key) {
ImageLoader.getInstance().getMemoryCache().remove(key);
}
@Override
public boolean put(String key, Bitmap value) {
if (key == null || value == null) return false;
return ImageLoader.getInstance().getMemoryCache().put(key, value);
}
@Override
public Collection<String> keys() {
return null;
}
@Override
public Bitmap get(String key) {
return ImageLoader.getInstance().getMemoryCache().get(key);
}
@Override
public void clear() {
ImageLoader.getInstance().getMemoryCache().clear();
}
};
}
return bitmapCache;
}
public final boolean put(String key, Bitmap value) { if(key != null && value != null) { synchronized(this) { this.size += this.sizeOf(key, value); Bitmap previous = (Bitmap)this.map.put(key, value); if(previous != null) { this.size -= this.sizeOf(key, previous); } } this.trimToSize(this.maxSize); return true; } else { throw new NullPointerException("key == null || value == null"); } }所以不难理解我们在用Universal-Image-Loader做图片加载的时候,有时候图片缓存超过阈值的时候,会去重新重服务器加载了
private void trimToSize(int maxSize) { while(true) { synchronized(this) { if(this.size < 0 || this.map.isEmpty() && this.size != 0) { throw new IllegalStateException(this.getClass().getName() + ".sizeOf() is reporting inconsistent results!"); } if(this.size > maxSize && !this.map.isEmpty()) { Entry toEvict = (Entry)this.map.entrySet().iterator().next(); if(toEvict != null) { String key = (String)toEvict.getKey(); Bitmap value = (Bitmap)toEvict.getValue(); this.map.remove(key); this.size -= this.sizeOf(key, value); continue; } } return; } } }
这时候我们会有一个以为,为什么遍历一下就可以将使用最少的bitmap缓存给剔除,不会误删到最近使用的bitmap缓存吗?首先,我们要清楚,LruMemoryCache定义的最近使用是指最近用get或put方式操作到的bitmap缓存。其次,之前我们直到LruMemoryCache的get操作其实是通过其内部字段LinkedHashMap.get(...)实现的,当LinkedHashMap的accessOrder==true时,每一次get或put操作都会将所操作项(图中第3项)移动到链表的尾部(见下图,链表头被认为是最少使用的,链表尾被认为是最常使用的。),每一次操作到的项我们都认为它是最近使用过的,当内存不够的时候被剔除的优先级最低。需要注意的是一开始的LinkedHashMap链表是按插入的顺序构成的,也就是第一个插入的项就在链表头,最后一个插入的就在链表尾。假设只要剔除图中的1,2项就能让LruMemoryCache小于原先限定的大小,那么我们只要从链表头遍历下去(从1→最后一项)那么就可以剔除使用最少的项了。
至此,我们就知道了LruMemoryCache缓存的整个原理,包括他怎么put、get、剔除一个元素的的策略。接下去,我们要开始分析默认的磁盘缓存策略了。
UIL中的磁盘缓存策略
幸好UIL提供了几种常见的磁盘缓存策略,你也可以自己去扩展,可以根据他提供的几种缓存策略做进一步的缓存值的限制,
FileCountLimitedDiscCache(可以设定缓存图片的个数,当超过设定值,删除掉最先加入到硬盘的文件) LimitedAgeDiscCache(设定文件存活的最长时间,当超过这个值,就删除该文件) TotalSizeLimitedDiscCache(设定缓存bitmap的最大值,当超过这个值,删除最先加入到硬盘的文件) UnlimitedDiscCache(这个缓存类没有任何的限制)在UIL中有着比较完整的存储策略,根据预先指定的空间大小,使用频率(生命周期),文件个数的约束条件,都有着对应的实现策略。最基础的接口DiscCacheAware和抽象类BaseDiscCache
接下来我们看一些硬盘缓存的一些策略:接下来就给大家分析分析硬盘缓存的策略,这个框架也提供了几种常见的缓存策略,当然如果你觉得都不符合你的要求,你也可以自己去扩展
下面我们就来分析分析TotalSizeLimitedDiscCache的源码实现
最后看一些我们需要怎么使用
配置:
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(YmatouApplication.instance()) .threadPriority(Thread.NORM_PRIORITY - 1) .memoryCache(new LruMemoryCache(getMemoryCacheSize())) .diskCacheSize(50 * ByteConstants.MB) .diskCacheFileNameGenerator(new Md5FileNameGenerator()) .diskCacheFileCount(200) .memoryCacheExtraOptions(480, 800) .threadPoolSize(3) .memoryCache(new WeakMemoryCache()) .tasksProcessingOrder(QueueProcessingType.FIFO) .defaultDisplayImageOptions(getSimpleOptions().build()) .build(); ImageLoader.getInstance().init(config);在做网络请求的时候,我们也做些配置,如网络请求之前默认什么背景,请求完成后,请求失败后怎么显示我们都可以在
defaultDisplayImageOptions(DisplayImageOptions.Builder builed)进行配置
加载图片:private static DisplayImageOptions.Builder getSimpleOptions() { DisplayImageOptions.Builder options = new DisplayImageOptions.Builder() .cacheInMemory(true) .cacheOnDisk(true) .considerExifParams(true) .imageScaleType(ImageScaleType.EXACTLY) .showImageOnLoading(R.drawable.image_loading) .showImageForEmptyUri(R.drawable.image_default) .showImageOnFail(R.drawable.image_failed) .resetViewBeforeLoading(true) .bitmapConfig(Bitmap.Config.ARGB_8888); return options; }
public static void imageloader(String url, ImageView imageView) { displayImage(url, imageView, null); }
Universal-Image-Loader源码分析,及常用的缓存策略
标签:
原文地址:http://blog.csdn.net/xiangzhihong8/article/details/51035137