UniversalImageLoader源码分析及常用的缓存策略.docx

上传人:b****6 文档编号:7348730 上传时间:2023-01-23 格式:DOCX 页数:17 大小:91.92KB
下载 相关 举报
UniversalImageLoader源码分析及常用的缓存策略.docx_第1页
第1页 / 共17页
UniversalImageLoader源码分析及常用的缓存策略.docx_第2页
第2页 / 共17页
UniversalImageLoader源码分析及常用的缓存策略.docx_第3页
第3页 / 共17页
UniversalImageLoader源码分析及常用的缓存策略.docx_第4页
第4页 / 共17页
UniversalImageLoader源码分析及常用的缓存策略.docx_第5页
第5页 / 共17页
点击查看更多>>
下载资源
资源描述

UniversalImageLoader源码分析及常用的缓存策略.docx

《UniversalImageLoader源码分析及常用的缓存策略.docx》由会员分享,可在线阅读,更多相关《UniversalImageLoader源码分析及常用的缓存策略.docx(17页珍藏版)》请在冰豆网上搜索。

UniversalImageLoader源码分析及常用的缓存策略.docx

UniversalImageLoader源码分析及常用的缓存策略

Universal-Image-Loader源码分析,及常用的缓存策略

讲到图片请求,主要涉及到网络请求,内存缓存,硬盘缓存等原理和4大引用的问题,概括起来主要有以下几个内容:

原理示意图

主体有三个,分别是UI,缓存模块和数据源(网络)。

它们之间的关系如下:

①UI:

请求数据,使用唯一的Key值索引MemoryCache中的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中的默认配置缓存策略进行分析。

viewsourceprint?

1.

ImageLoaderConfigurationconfig=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。

viewsourceprint?

1.

publicinterfaceMemoryCacheextendsMemoryCacheAware

下面给出继承关系图

通过跟踪LruMemoryCache.get()的代码我们发现,LruMemoryCache声称保留在空间有限的情况下保留最近使用过的Bitmap,这是一个LinkedHashMap,Icache的源码缓存的代码如下:

@Override

publicICachegetBitmapCache(){

if(bitmapCache==null){

bitmapCache=newICache(){

@Override

publicvoidremove(Stringkey){

ImageLoader.getInstance().getMemoryCache().remove(key);

}

@Override

publicbooleanput(Stringkey,Bitmapvalue){

if(key==null||value==null)returnfalse;

returnImageLoader.getInstance().getMemoryCache().put(key,value);

}

@Override

publicCollectionkeys(){

returnnull;

}

@Override

publicBitmapget(Stringkey){

returnImageLoader.getInstance().getMemoryCache().get(key);

}

@Override

publicvoidclear(){

ImageLoader.getInstance().getMemoryCache().clear();

}

};

}

returnbitmapCache;

}

看到这里我们就清楚LruMemoryCache使用LinkedHashMap来缓存数据,在LinkedHashMap.get()方法执行后,LinkedHashMap中entry的顺序会得到调整。

那么我们怎么保证最近使用的项不会被剔除呢?

接下去,让我们看看LruMemoryCache.put(...)。

注意到代码第8行中的size+=sizeOf(key,value),这个size是什么呢?

我们注意到在第19行有一个trimToSize(maxSize),trimToSize(...)这个函数就是用来限定LruMemoryCache的大小不要超过用户限定的大小,cache的大小由用户在LruMemoryCache刚开始初始化的时候限定。

publicfinalbooleanput(Stringkey,Bitmapvalue){

if(key!

=null&&value!

=null){

synchronized(this){

this.size+=this.sizeOf(key,value);

Bitmapprevious=(Bitmap)this.map.put(key,value);

if(previous!

=null){

this.size-=this.sizeOf(key,previous);

}

}

this.trimToSize(this.maxSize);

returntrue;

}else{

thrownewNullPointerException("key==null||value==null");

}

}

所以不难理解我们在用Universal-Image-Loader做图片加载的时候,有时候图片缓存超过阈值的时候,会去重新重服务器加载了

当Bitmap缓存的大小超过原来设定的maxSize时应该是在trimToSize(...)这个函数中做到的。

这个函数做的事情也简单,遍历map,将多余的项(代码中对应toEvict)剔除掉,直到当前cache的大小等于或小于限定的大小。

privatevoidtrimToSize(intmaxSize){

while(true){

synchronized(this){

if(this.size<0||this.map.isEmpty()&&this.size!

=0){

thrownewIllegalStateException(this.getClass().getName()+".sizeOf()isreportinginconsistentresults!

");

}

if(this.size>maxSize&&!

this.map.isEmpty()){

EntrytoEvict=(Entry)this.map.entrySet().iterator().next();

if(toEvict!

=null){

Stringkey=(String)toEvict.getKey();

Bitmapvalue=(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(这个缓存类没有任何的限制)

UsingFreqLimitedMemoryCache(如果缓存的图片总量超过限定值,先删除使用频率最小的bitmap)

LRULimitedMemoryCache(这个也是使用的lru算法,和LruMemoryCache不同的是,他缓存的是bitmap的弱引用)

FIFOLimitedMemoryCache(先进先出的缓存策略,当超过设定值,先删除最先加入缓存的bitmap)

LargestLimitedMemoryCache(当超过缓存限定值,先删除最大的bitmap对象)

LimitedAgeMemoryCache(当bitmap加入缓存中的时间超过我们设定的值,将其删除)

在UIL中有着比较完整的存储策略,根据预先指定的空间大小,使用频率(生命周期),文件个数的约束条件,都有着对应的实现策略。

最基础的接口DiscCacheAware和抽象类BaseDiscCache

接下来我们看一些硬盘缓存的一些策略:

接下来就给大家分析分析硬盘缓存的策略,这个框架也提供了几种常见的缓存策略,当然如果你觉得都不符合你的要求,你也可以自己去扩展

FileCountLimitedDiscCache(可以设定缓存图片的个数,当超过设定值,删除掉最先加入到硬盘的文件)

LimitedAgeDiscCache(设定文件存活的最长时间,当超过这个值,就删除该文件)

TotalSizeLimitedDiscCache(设定缓存bitmap的最大值,当超过这个值,删除最先加入到硬盘的文件)

UnlimitedDiscCache(这个缓存类没有任何的限制)

下面我们就来分析分析TotalSizeLimitedDiscCache的源码实现

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

/*******************************************************************************

*Copyright2011-2013SergeyTarasevich

*

*LicensedundertheApacheLicense,Version2.0(the"License");

*youmaynotusethisfileexceptincompliancewiththeLicense.

*YoumayobtainacopyoftheLicenseat

*

*http:

//www.apache.org/licenses/LICENSE-2.0

*

*Unlessrequiredbyapplicablelaworagreedtoinwriting,software

*distributedundertheLicenseisdistributedonan"ASIS"BASIS,

*WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied.

*SeetheLicenseforthespecificlanguagegoverningpermissionsand

*limitationsundertheLicense.

*******************************************************************************/

packagecom.nostra13.universalimageloader.cache.disc.impl;

importcom.nostra13.universalimageloader.cache.disc.LimitedDiscCache;

importcom.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;

importcom.nostra13.universalimageloader.core.DefaultConfigurationFactory;

importcom.nostra13.universalimageloader.utils.L;

importjava.io.File;

/**

*Disccachelimitedbytotalcachesize.Ifcachesizeexceedsspecifiedlimitthenfilewiththemostoldestlast

*usagedatewillbedeleted.

*

*@authorSergeyTarasevich(nostra13[at]gmail[dot]com)

*@seeLimitedDiscCache

*@since1.0.0

*/

publicclassTotalSizeLimitedDiscCacheextendsLimitedDiscCache{

privatestaticfinalintMIN_NORMAL_CACHE_SIZE_IN_MB=2;

privatestaticfinalintMIN_NORMAL_CACHE_SIZE=MIN_NORMAL_CACHE_SIZE_IN_MB*1024*1024;

/**

*@paramcacheDirDirectoryforfilecaching.Important:

Specifyseparatefolderforcachedfiles.It's

*neededforrightcachelimitwork.

*@parammaxCacheSizeMaximumcachedirectorysize(inbytes).Ifcachesizeexceedsthislimitthenfilewiththe

*mostoldestlastusagedatewillbedeleted.

*/

publicTotalSizeLimitedDiscCache(FilecacheDir,intmaxCacheSize){

this(cacheDir,DefaultConfigurationFactory.createFileNameGenerator(),maxCacheSize);

}

/**

*@paramcacheDirDirectoryforfilecaching.Important:

Specifyseparatefolderforcachedfiles.It's

*neededforrightcachelimitwork.

*@paramfileNameGeneratorNamegeneratorforcachedfiles

*@parammaxCacheSizeMaximumcachedirectorysize(inbytes).Ifcachesizeexceedsthislimitthenfilewiththe

*mostoldestlastusagedatewillbedeleted.

*/

publicTotalSizeLimitedDiscCache(FilecacheDir,FileNameGeneratorfileNameGenerator,intmaxCacheSize){

super(cacheDir,fileNameGenerator,maxCacheSize);

if(maxCacheSize

L.w("Yousettoosmalldisccachesize(lessthan%1$dMb)",MIN_NORMAL_CACHE_SIZE_IN_MB);

}

}

@Override

protectedintgetSize(Filefile){

return(int)file.length();

}

}

这个类是继承LimitedDiscCache,除了两个构造函数之外,还重写了getSize()方法,返回文件的大小,接下来我们就来看看LimitedDiscCache

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

/*******************************************************************************

*Copyright2011-2013SergeyTarasevich

*

*LicensedundertheApacheLicense,Version2.0(the"License");

*youmaynotusethisfileexceptincompliancewiththeLicense.

*YoumayobtainacopyoftheLicenseat

*

*http:

//www.apache.org/licenses/LICENSE-2.0

*

*Unlessrequiredbyapplicablelaworagreedtoinwriting,software

*distributedundertheLicenseisdistributedonan"ASIS"BASIS,

*WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied.

*SeetheLicenseforthespecificlanguagegoverningpermissionsand

*limitationsundertheLicense.

*******************************************************************************/

packagecom.nostra13.universalimageloader.cache.disc;

importcom.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;

importcom.nostra13.universalimageloader.core.DefaultConfigurationFactory;

importjava.io.File;

importjava.util.Collections;

importjava.util.HashMap;

importjava.util.Map;

importjava.util.Map.Entry;

importjava.util.Set;

importjava.util.concurrent.atomic.AtomicInteger;

/**

*Abstractdisccachelimitedbysomeparameter.Ifcacheexceedsspecifiedlimitthenfilew

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高中教育 > 语文

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1