universal-Image-Loader
在做安卓开发的时候,肯定会接触到异步夹在图片或者大量加载图片的情况。作为新手肯定会遇到很多问题,如OOM或者图片错误等问题。因此有许多图片加载的开源框架供我们使用,今天学习一下universal-Image-Loader框架。
这个框架的github地址为 https://github.com/nostra13/Android-Universal-Image-Loader。在github上的介绍我们可以看到,该框架支持的URL类型很多,从web、sdcard、contentProvider、assets、drawable等都可以加载图片
"http://site.com/image.png" // from Web
"file:///mnt/sdcard/image.png" // from SD card
"file:///mnt/sdcard/video.mp4" // from SD card (video thumbnail)
"content://media/external/images/media/13" // from content provider
"content://media/external/video/media/13" // from content provider (video thumbnail)
"assets://image.png" // from assets
"drawable://" + R.drawable.img // from drawables (non-9patch images)
另外作者提示我们
Use drawable:// only if you really need it! Always consider the native way to load drawables - ImageView.setImageResource(…) instead of using of ImageLoader.
一般情况下加载drawable文件里面的图片时,都使用setImageResource(…),除非really need universal-Image-Loader时再使用。
universal-Image-Loader的特征
- 多线程下载图片,图片可以来源于网络,文件系统,项目文件夹assets中以及drawable中等
- 支持随意的配置ImageLoader,例如线程池,图片下载器,内存缓存策略,硬盘缓存策略,图片显示选项以及其他的一些配置
- 支持图片的内存缓存,文件系统缓存或者SD卡缓存
- 支持图片下载过程的监听
- 根据控件(ImageView)的大小对Bitmap进行裁剪,减少Bitmap占用过多的内存
- 较好的控制图片的加载过程,例如暂停图片加载,重新开始加载图片,一般使用在ListView,GridView中,滑动过程中暂停加载图片,停止滑动的时候去加载图片
- 提供在较慢的网络下对图片进行加载
universal-Image-Loader的使用
要使用这个开源框架,eclipse上需要将这个框架的jar包导入libs文件,并且Add to build path.然后再项目属性里面Order and Export属性里面将这个框架前面的对勾打上。至于Android studio就不知道了,由于电脑较渣,运行太卡。
新建一个MyApplication类,在这里面进行universal-Image-Loader的配置
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
ImageLoaderConfiguration config=ImageLoaderConfiguration.createDefault(this);
ImageLoader.getInstance().init(config);
}
}
ImageLoaderConfiguration是ImageLoader的配置参数,在这里直接使用了createDefault()创建了一个默认的,一般默认的就可以了,当然也可以自定义参数,可设置的参数如下
File cacheDir = StorageUtils.getCacheDirectory(context);
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
.memoryCacheExtraOptions(480, 800) // default = device screen dimensions
.diskCacheExtraOptions(480, 800, CompressFormat.JPEG, 75, null)
.taskExecutor(...)
.taskExecutorForCachedImages(...)
.threadPoolSize(3) // default
.threadPriority(Thread.NORM_PRIORITY - 1) // default
.tasksProcessingOrder(QueueProcessingType.FIFO) // default
.denyCacheImageMultipleSizesInMemory()
.memoryCache(new LruMemoryCache(2 * 1024 * 1024))
.memoryCacheSize(2 * 1024 * 1024)
.memoryCacheSizePercentage(13) // default
.diskCache(new UnlimitedDiscCache(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()
.build();
然后调用ImageLoader的init方法将配置的参数传递进去,getInstance方法表明ImageLoader是单例模式。
然后注意配置AndroidManifest.xml文件,将MyApplication配置上,并且加上网络访问和读取外部存储的权限。
开始使用
布局文件,一个ImageView
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="${relativePackage}.${activityClass}" >
<ImageView
android:id="@+id/third_show_ImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
></ImageView>
</RelativeLayout>
ImageLoader主要有以下下载图片的方法
其中loadImageSync方法是同步的。
首先使用loadImage方法
mShowImageView=(ImageView) findViewById(R.id.third_show_ImageView);
String imageUrl="http://d.hiphotos.baidu.com/baike/w%3D268%3Bg%3D0/sign=abab84ca2af5e0feee188e07645b5395/a8014c086e061d9551cfbceb7cf40ad163d9ca5c.jpg";
ImageLoader.getInstance().loadImage(imageUrl, new ImageLoadingListener() {
@Override
public void onLoadingCancelled(String arg0, View arg1) {
}
@Override
public void onLoadingComplete(String arg0, View arg1, Bitmap arg2) {
Log.i(TAG, Thread.currentThread().getName()+"--------------");
mShowImageView.setImageBitmap(arg2);
}
@Override
public void onLoadingFailed(String arg0, View arg1, FailReason arg2) {
}
@Override
public void onLoadingStarted(String arg0, View arg1) {
}
});
可以看到,只需要将需要下载的图片的url地址传进去,并且监听图片下载,还可以重写四个方法,对下载开始,下载取消,下载完成,下载失败,分别进行处理。下载完成的三个参数分别为
String imageUri;
View view
Bitmap loadedImage;
如果想简单一点,只对下载完成进行处理
ImageLoader.getInstance().loadImage(imageUrl, new SimpleImageLoadingListener(){
@Override
public void onLoadingComplete(String imageUri, View view,
Bitmap loadedImage) {
super.onLoadingComplete(imageUri, view, loadedImage);
mShowImageView.setImageBitmap(loadedImage);
}
});
只需要监听SimpleImageLoadingListener接口就可以了,只需要实现下载完成的方法。
如果需要指定图片的大小,初始化一个ImageSize,设置宽和高,然后调用ImageLoader的另一个重载的方法将size传递进去。
ImageSize size=new ImageSize(200, 200);
ImageLoader.getInstance().loadImage(imageUrl, size,new SimpleImageLoadingListener(){
@Override
public void onLoadingComplete(String imageUri, View view,
Bitmap loadedImage) {
super.onLoadingComplete(imageUri, view, loadedImage);
mShowImageView.setImageBitmap(loadedImage);
}
});
以上只是简单使用,如果需要配置信息
可以配置的信息有
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.ic_stub) // resource or drawable
.showImageForEmptyUri(R.drawable.ic_empty) // resource or drawable
.showImageOnFail(R.drawable.ic_error) // resource or drawable
.resetViewBeforeLoading(false) // default
.delayBeforeLoading(1000)
.cacheInMemory(false) // default
.cacheOnDisk(false) // default
.preProcessor(...)
.postProcessor(...)
.extraForDownloader(...)
.considerExifParams(false) // default
.imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) // default
.bitmapConfig(Bitmap.Config.ARGB_8888) // default
.decodingOptions(...)
.displayer(new SimpleBitmapDisplayer()) // default
.handler(new Handler()) // default
.build();
将上面的代码修改一下
ImageSize size=new ImageSize(200, 200);
//显示图片的配置
DisplayImageOptions options=new DisplayImageOptions.Builder()
//使用内存缓存
.cacheInMemory(true)
//使用文件缓存
.cacheOnDisc(true)
.bitmapConfig(Bitmap.Config.RGB_565)
.build();
ImageLoader.getInstance().loadImage(imageUrl, size, options,new SimpleImageLoadingListener(){
@Override
public void onLoadingComplete(String imageUri, View view,
Bitmap loadedImage) {
super.onLoadingComplete(imageUri, view, loadedImage);
mShowImageView.setImageBitmap(loadedImage);
}
});
这里配置将图片加载到缓存中,就不需要每次从网络上下载了,但是DisplayImageOptions的一些配置对loadImage是无效的,比如设置下载失败的图片和正在下载的图片等
使用displayImage()方法
修改代码
DisplayImageOptions options2=new DisplayImageOptions.Builder()
.cacheInMemory(true)
.cacheOnDisc(true)
.bitmapConfig(Bitmap.Config.RGB_565)
.showImageOnLoading(R.drawable.loading_default_small)
.showImageOnFail(R.drawable.icon_fail)
.build();
ImageLoader.getInstance().displayImage(imageUrl, mShowImageView, options2,new SimpleImageLoadingListener(), new ImageLoadingProgressListener() {
@Override
public void onProgressUpdate(String imageUrl, View view, int current, int total) {
}
});
可以看到使用非常简单,并且它会根据控件的大小和ImageScaleTyle的类型来裁剪图片,而且不需要手动设置bitmap对象,只需要将控件作为参数传递进去就可以了。在我们加载图片的时候,经常需要显示进度,displayImage方法也提供了这样的接口,使用方法上面的代码已经实现了。
加载其他来源的图片
加载文件系统
String imagePath = "/mnt/sdcard/image.png";
String imageUrl = Scheme.FILE.wrap(imagePath);
加载contentProvider
String contentprividerUrl = "content://media/external/audio/albumart/13";
加载assets
String assetsUrl = Scheme.ASSETS.wrap("image.png");
加载drawable
String drawableUrl = Scheme.DRAWABLE.wrap("R.drawable.image");
可以看到,除了contentProvider外,其他的都是在原地址上使用Scheme进行wrap一下生成新的Url即可。
GirdView,ListView加载图片
而当我们快速滑动GridView,ListView,我们希望能停止图片的加载,而在GridView,ListView停止滑动的时候加载当前界面的图片,这个框架当然也提供这个功能,使用起来也很简单,它提供了PauseOnScrollListener这个类来控制ListView,GridView滑动过程中停止去加载图片,该类使用的是代理模式
listView.setOnScrollListener(new PauseOnScrollListener(imageLoader, pauseOnScroll, pauseOnFling));
gridView.setOnScrollListener(new PauseOnScrollListener(imageLoader, pauseOnScroll, pauseOnFling));
第一个参数就是我们的图片加载对象ImageLoader, 第二个是控制是否在滑动过程中暂停加载图片,如果需要暂停传true就行了,第三个参数控制猛的滑动界面的时候图片是否加载
OutOfMemoryError
虽然这个框架有很好的缓存机制,有效的避免了OOM的产生,一般的情况下产生OOM的概率比较小,但是并不能保证OutOfMemoryError永远不发生,这个框架对于OutOfMemoryError做了简单的catch,保证我们的程序遇到OOM而不被crash掉,但是如果我们使用该框架经常发生OOM,我们应该怎么去改善呢?
- 减少线程池中线程的个数,在ImageLoaderConfiguration中的(.threadPoolSize)中配置,推荐配置1-5
- 在DisplayImageOptions选项中配置bitmapConfig为Bitmap.Config.RGB_565,因为默认是ARGB_8888, 使用RGB_565会比使用ARGB_8888少消耗2倍的内存
- 在ImageLoaderConfiguration中配置图片的内存缓存为memoryCache(new WeakMemoryCache()) 或者不使用内存缓存
- 在DisplayImageOptions选项中设置.imageScaleType
(ImageScaleType.IN_SAMPLE_INT)
或者imageScaleType(ImageScaleType.EXACTLY)
推荐使用displayImage()
displayImage()方法中,对ImageView对象使用的是Weak references,方便垃圾回收器回收ImageView对象,如果我们要加载固定大小的图片的时候,使用loadImage()方法需要传递一个ImageSize对象,而displayImage()方法会根据ImageView对象的测量值,或者android:layout_width and android:layout_height设定的值,或者android:maxWidth and/or android:maxHeight设定的值来裁剪图片