Android开发中我们会经常遇到图片过多或操作不当造成OOM异常,有时虽然是解决了这个问题但却会影响程序的运行效率,例如:当用户在快速滑动滚动条的过程中,我们程序在仍在艰难的加载服务器端的图片,这样给用户造成了极不好的体验。其实网络上关于图片的异步加载和缓存的讲解很多,但是其实,写一个这方面的程序还是比较麻烦的,要考虑多线程,缓存,内存溢出等很多方面,针对这一光大开发者都会遇到的问题,开源框架ImageLoader便能很好的解决这一问题
1. 功能特性:
多线程异步加载和显示图片(图片来源于网络、sd卡、assets文件夹,drawable文件夹(不能加载9patch),新增加载视频缩略图)。
支持二级缓存,即内存缓存(软引用)及本地缓存。
对加载过程实现监听和事件处理。通过“listener”监视加载的过程,可以暂停加载图片,在经常使用的ListView、GridView中,可以设置滑动时暂停加。载,停止滑动时加载图片(便于节约流量,在一些优化中可以使用)。
高度可定制化(可以根据自己的需求进行各种配置,如:线程池,图片下载器,内存缓存策略等)。
2. UIL原理:
a.UI:请求数据,使用唯一的Key值索引MemoryCache中的Bitmap。
b.内存缓存:缓存搜索,如果能找到Key值对应的Bitmap,则返回数据。否则执行c。
c.硬盘存储:使用唯一Key值对应的文件名,检索SDCard上的文件。如果有对应文件,使用BitmapFactory.decode*方法,解码Bitmap并返回数据,同时将数据写入缓存。如果没有对应文件,执行d。
d.下载图片:启动异步线程,从数据源下载数据(Web)。
e.若下载成功,将数据同时写入硬盘和缓存,并将Bitmap显示在UI中。
3. 配置和使用:
- 新建一个类继承Application,实现初始化ImageLoader
public class AppContext extends Application {
private static Context mContext;
@Override
public void onCreate() {
super.onCreate();
mContext = this;
initImageLoader();
}
public static Context getAppContext() {
return context;
}
private final static void initImageLoader() {
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
.defaultDisplayImageOptions(getDefaultDisplayOption())//显示图片的参数,传入自己配置过得DisplayImageOption对象
.memoryCache(new LruMemoryCache(2 * 1024 * 1024)) //缓存策略
.memoryCacheExtraOptions(480, 800) //即保存的每个缓存文件的最大长宽
.threadPoolSize(5) //线程池内线程的数量,默认是3
.threadPriority(Thread.NORM_PRIORITY - 2) //当同一个Uri获取不同大小的图片,缓存到内存时,只缓存一个。默认会缓存多个不同的大小的相同图片
.denyCacheImageMultipleSizesInMemory() //拒绝同一个url缓存多个图片
.diskCacheSize(50 * 1024 * 1024) //设置磁盘缓存大小 50M
.diskCacheFileNameGenerator(new Md5FileNameGenerator()) //将保存的时候的URI名称用MD5 加密
.imageDownloader(new BaseImageDownloader(this, 5 * 1000, 30 * 1000)) // connectTimeout (5 s), readTimeout (30 s)超时时间
.tasksProcessingOrder(QueueProcessingType.LIFO)//设置图片下载和显示的工作队列排序
.build();
ImageLoader.getInstance().init(config);
}
private final static DisplayImageOptions getDefaultDisplayOption() {
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageForEmptyUri(R.drawable.empty_photo) // 设置图片Uri为空或是错误的时候显示的图片
.showImageOnFail(R.drawable.empty_photo) // 设置图片加载或解码过程中发生错误显示的图片
.cacheInMemory(true) // 设置下载的图片是否缓存在内存中
.cacheOnDisk(true) // 设置下载的图片是否缓存在SD卡中
.showImageOnLoading(R.drawable.empty_photo)
.build();
return options;
}
}
2.在清单文件AndroidManifest.xml中的application节点中加入
android:name=".MyApplication"
3.在Activity中使用ImageLoader:
ImageLoader.getInstance().displayImage(uri, imageView);
清除缓存的方法有:
ImageLoader.getInstance().clearDiskCache();
ImageLoader.getInstance().clearMemoryCache();
4. Universal-Image-Loader的内存缓存策略:
1.强引用缓存:
LruMemoryCache(这个类就是这个开源框架默认的内存缓存类,缓存的是bitmap的强引用)
好处:滑动时图片不会容易被清除,体验好;坏处:容易出现OOM
2.使用强引用和弱引用相结合的缓存有:
UsingFreqLimitedMemoryCache(如果缓存的图片总量超过限定值,先删除使用频率最小的bitmap)
LRULimitedMemoryCache(这个也是使用的lru算法,和LruMemoryCache不同的是,他缓存的是bitmap的弱引用)
FIFOLimitedMemoryCache(先进先出的缓存策略,当超过设定值,先删除最先加入缓存的bitmap)
LargestLimitedMemoryCache(当超过缓存限定值,先删除最大的bitmap对象)
LimitedAgeMemoryCache(当 bitmap加入缓存中的时间超过我们设定的值,将其删除)
3.只使用弱引用缓存:
WeakMemoryCache(这个类缓存bitmap的总大小没有限制,唯一不足的地方就是不稳定,缓存的图片容易被回收掉)
好处:对缓存的bitmap总大小无限制;坏处:使得图片不稳定,图片极其容易被回收
*注意:不要使用.cacheInMemory();*
5. Universal-Image-Loader的硬盘缓存策略
FileCountLimitedDiscCache(可以设定缓存图片的个数,当超过设定值,删除掉最先加入到硬盘的文件)
LimitedAgeDiscCache(设定文件存活的最长时间,当超过这个值,就删除该文件)
TotalSizeLimitedDiscCache(设定缓存bitmap的最大值,当超过这个值,删除最先加入到硬盘的文件)
UnlimitedDiscCache(这个缓存类没有任何的限制)
在ImageLoaderConfiguration中配置了diskCacheSize和diskCacheFileCount,他就使用的是LruDiscCache,否则使用的是UnlimitedDiscCache
在最新的源码中还有一个硬盘缓存类可以配置,那就是LimitedAgeDiscCache,可以在ImageLoaderConfiguration.diskCache(...)配置
避免OOM
为了避免OOM,可以修改下面的配置方案:
.memoryCacheExtraOptions(480, 800) //即保存的每个缓存文件的最大长宽,大小自行配制
.threadPriority(Thread.NORM_PRIORITY - 2) //线程池中线程的个数,一般配置1-5,这里配的是3
.memoryCache(new LRULimitedMemoryCache(40*1024*1024)) //改变缓存策略,可以用弱引用