未经本人授权,不得转载!否则必将维权到底
平时做开发的时候, App 中肯定有异步加载图片,或者加载大量图片的需求。而加载图片常常会遇到许多的问题,比如说图片的错乱,OOM 等问题。相信大多数 Android 程序员都是用网络上最流行的图片加载库来实现咱们的需求。比较著名的就是 Universal-Image-Loader 、Fresco 、Picasso 、Glide 。今天首先来讲下大名鼎鼎的 Universal-Image-Loader
###一、特征:
多线程下载图片,图片可以来源于网络,文件系统,项目文件夹 assets 中以及 drawable 中等
支持随意的配置 ImageLoader,例如线程池,图片下载器,内存缓存策略,硬盘缓存策略,图片显示选项以及其他的一些配置
支持图片的内存缓存,文件系统缓存或者 SD 卡缓存
支持图片下载过程的监听
根据控件 ImageView 的大小对 Bitmap 进行裁剪,减少 Bitmap 占用过多的内存
较好的控制图片的加载过程,例如暂停图片加载,重新开始加载图片,一般使用在 ListView , GridView 中,滑动过程中暂停加载图片,停止滑动的时候去加载图片
提供在较慢的网络下对图片进行加载
###二、设计原理:
在显示图片的时候,它会先在内存中查找;如果没有,就去本地查找;如果还没有,就开一个新的线程去下载这张图片,下载成功会把图片同时缓存到内存和本地。基于这个原理,我们可以在每次退出一个页面的时候,把 ImageLoader 内存中的缓存全都清除,这样就节省了大量内存,反正下次再用到的时候从本地再取出来就是了。此外,由于 ImageLoader 对图片是软引用的形式,所以内存中的图片会在内存不足的时候被系统回收(内存足够的时候不会对其进行垃圾回收)—— 包建强《 App 研发录》
###三、使用步骤:官方教程 Quick Setup
添加依赖:compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
添加权限:
<manifest>
<!-- Include following permission if you load images from Internet -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- Include following permission if you want to cache images on SD card -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
</manifest>
在Application或Activity中进行初始化配置:
// ImageLoaderConfiguration 详细配置
File cacheDir = StorageUtils.getOwnCacheDirectory(getApplicationContext(), “imageloader/Cache”); // 自定义缓存文件夹
ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Builder(context)
.memoryCacheExtraOptions(480, 800) // 指定缓存到内存时图片的大小,默认是屏幕尺寸的长宽
.diskCacheExtraOptions(480, 800, null) // 指定缓存到硬盘时图片的大小,并不建议使用
.taskExecutor(new Executor()) // 自定义一个线程来加载和显示图片
.taskExecutorForCachedImages(new Executor())// 自定义一个线程来缓存图片
.threadPoolSize(3) // default, 指定线程池大小
.threadPriority(Thread.NORM_PRIORITY - 2) // default ,指定线程优先级
.tasksProcessingOrder(QueueProcessingType.FIFO) // default , 指定加载显示图片的任务队列的类型
.denyCacheImageMultipleSizesInMemory() // 禁止在内存中缓存同一张图片的多个尺寸类型
.memoryCache(new LruMemoryCache(2 * 1024 * 1024)) // 指定内存缓存的大小,默认值为1/8 应用的最大可用内存
.memoryCacheSize(2 * 1024 * 1024)
.memoryCacheSizePercentage(13) // default
.diskCache(new UnlimitedDiskCache(cacheDir)) // default , 指定硬盘缓存的地址
.diskCacheSize(50 * 1024 * 1024) // 指定硬盘缓存的大小
.diskCacheFileCount(100) // 指定硬盘缓存的文件个数
.diskCacheFileNameGenerator(new HashCodeFileNameGenerator()) // default , 指定硬盘缓存时文件名的生成器
.imageDownloader(new BaseImageDownloader(context)) // default , 指定图片下载器
.imageDecoder(new BaseImageDecoder()) // default , 指定图片解码器
.defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // default , 指定图片显示的配置
.writeDebugLogs() // 是否显示Log
.build();
//-----------------------------------------------------------------------------------------------------------------
// ImageLoaderConfiguration 简单初始化
ImageLoaderConfiguration configuration = ImageLoaderConfiguration.createDefault(this);
// 初始化配置
ImageLoader.getInstance().init(configuration);
显示图片的常用方法:
ImageLoader.getInstance().loadImage(String uri, ImageLoadingListener listener);
ImageLoader.getInstance().displayImage(String uri, ImageView imageView);
ImageLoader.getInstance().displayImage(String uri, ImageView imageView, DisplayImageOptions options)
ImageLoader.getInstance().displayImage(String uri, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener, ImageLoadingProgressListener progressListener);
DisplayImageOptions 参数详解:
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.ic_stub) // 图片正在加载时显示的图片资源ID
.showImageForEmptyUri(R.drawable.ic_empty) // URI为空时显示的图片资源ID
.showImageOnFail(R.drawable.ic_error) // 图片加载失败时显示的图片资源ID
.resetViewBeforeLoading(false) // default 图片在下载前是否重置,复位
.delayBeforeLoading(1000) // 图片开始加载前的延时.默认是0
.cacheInMemory(false) // default , 是否缓存在内存中, 默认不缓存
.cacheOnDisk(false) // default , 是否缓存在硬盘 , 默认不缓存
.preProcessor(new BitmapProcessor) // 设置图片缓存在内存前的图片处理器
.postProcessor(new BitmapProcessor) // 设置图片在缓存到内存以后 , 显示在界面之前的图片处理器
.extraForDownloader(...) // 为图片下载设置辅助参数
.considerExifParams(false) // default , 设置是否考虑JPEG图片的EXIF参数信息,默认不考虑
.imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) // default , 指定图片缩放的方式,ListView/GridView/Gallery推荐使用此默认值
.bitmapConfig(Bitmap.Config.ARGB_8888) // default , 指定图片的质量,默认是 ARGB_8888
.decodingOptions(...) // 指定图片的解码方式
.displayer(new SimpleBitmapDisplayer()) // default , 设置图片显示的方式,用于自定义
.handler(new Handler()) // default ,设置图片显示的方式和ImageLoadingListener的监听, 用于自定义
.build();
6.特殊用法:
a.显示圆形图片.使用该效果,必须显式指定图片的宽高
DisplayImageOptions options = new DisplayImageOptions.Builder()
.displayer(new CircleBitmapDisplayer())
.build();
b.显示圆角图片.使用该效果,必须显式指定图片的宽高
DisplayImageOptions options = new DisplayImageOptions.Builder()
.displayer(new RoundedBitmapDisplayer(90))//指定圆角半径
.build();
c.显示圆角缩放图片.使用该效果,必须显式指定图片的宽高
DisplayImageOptions options = new DisplayImageOptions.Builder()
.displayer(new RoundedVignetteBitmapDisplayer(90,180))
.build();
d.显示渐显图片
DisplayImageOptions options = new DisplayImageOptions.Builder()
.displayer(new FadeInBitmapDisplayer(3000))
.build();
###三、ImageLoader 三大组件分析:
ImageLoader 由三大组件组成:
从三者的协作关系上看,他们有点像厨房规定、厨师、客户个人口味之间的关系。ImageLoaderConfiguration 就像是厨房里面的规定,每一个厨师要怎么着装,要怎么保持厨房的干净,这是针对每一个厨师都适用的规定,而且不允许个性化改变。ImageLoader 就像是具体做菜的厨师,负责具体菜谱的制作。 DisplayImageOptions 就像每个客户的偏好,根据客户是重口味还是清淡,每一个 ImageLoader 根据 DisplayImageOptions 的要求具体执行。
###四、ImageLoader 具体使用:
ImageLoader.getInstance().displayImage(imageUrl, imageView);
// imageUrl代表图片的URL地址,imageView代表承载图片的IMAGEVIEW控件 , options代表DisplayImageOptions配置文件
ImageLoader.getInstance().displayImage(imageUrl, imageView,options);
imageLoader.displayImage(imageUrl, imageView, options, new ImageLoadingListener() {
@Override
public void onLoadingStarted() {
//开始加载的时候执行
}
@Override
public void onLoadingFailed(FailReason failReason) {
//加载失败的时候执行
}
@Override
public void onLoadingComplete(Bitmap loadedImage) {
//加载成功的时候执行
}
@Override
public void onLoadingCancelled() {
//加载取消的时候执行
}});
###五、ImageLoader 优化:
1、 ImageLoader 很强大,但一直把图片缓存在内存中,会导致内存占用过高。虽然对图片的引用是软引用,软引用在内存不够的时候会被 GC,但我们还是希望减少 GC 的次数,所以要经常手动清理 ImageLoader 中的缓存。我们应该在 BaseActivity 中的 onDestroy() 方法中,执行 ImageLoader 的 clearMemoryCache() 方法,以确保页面销毁的时候,把为了显示这个页面而增加的内存缓存清楚。这样,即使下一个页面还需要复用之前加载过的图片时,虽然内存中没有了,但是本地缓存依然存在。
@Override
protected void onDestroy() {
//回收该页面缓存在内存的图片
imageLoader.clearMemoryCache();
super.onDestroy();
}
2、虽然 Universal-Image-Loader 有很好的缓存机制,有效的避免了 OOM 的产生,一般的情况下产生 OOM 的概率比较小,但是并不能保证 OutOfMemoryError 永远不发生,这个框架对于 OutOfMemoryError 只做了简单的 catch ,保证我们的程序遇到 OOM 而不被 crash 掉,但是如果我们使用该框架经常发生 OOM,我们应该怎么去改善呢?
###六、结语:
Universal-Image-Loader 已经出现很多年了,直到现在为止还是有不少公司(比如我们公司~)出于项目的稳定性的考虑,继续使用该图片加载库,所以还是很有必要好好学习一下的。
本文参考:
Android 开源框架Universal-Image-Loader完全解析(一)基本介绍及使用
Android-Universal-Image-Loader三大组件DisplayImageOptions、ImageLoader、ImageLoaderConfiguration详解
Android-Universal-Image-Loader 图片异步加载类库的使用(超详细配置)
推荐阅读:
Java基础 之软引用、弱引用、虚引用