Universal-Image-Loader完全解析(上)

基本介绍及使用

大家平时做项目的时候,或多或少都会接触到异步加载图片,或者大量加载图片的问题,而加载图片时候经常会遇到各种问题,如oom,图片加载混乱等。对于刚入门的新手来说,这些问题目前解决起来还比较因难,因此放多开源图片加载的框架就应运而生,其中Universal-Image-Loader就是里面的佼佼者。今天我们主要是针对这个开源框架进行解析,此框架的源码存在在Github上面,具体地址:https://github.com/nostra13/Android-Universal-Image-Loader,我们可以先看看这个框架有哪些特点:

  1. 多线程图片下载,图片可来自网络、项目文件夹assets中以及drawable中等
  2. 支持随意配置ImageLoader,例如线程池,内存缓存策略,硬盘缓存策略等
  3. 支持图片的内存缓存、文件缓存以及SD卡缓存等
  4. 支持加载图片过程中的各种事件的监听
  5. 能根据ImageView的大小对Bitmap进行裁剪,减少Bitmap占用过多的内存
  6. 支持控制图片的加载过程,例如暂停图片加载、重新加载图片,一般在ListView或者 GridView等滑动时暂停加载图片,停止滑动时再去加载图片
  7. 提供在较慢的网络下对图片进行加载

接下来我们进行这种开源框架的简单使用吧!

添加Jar包

新建一个Android项目,并在上面的地址中下载框架项目的jar包,然后导入到libs工程目录下

可以新建立一个MyApplication继承Application,并在OnCreate中创建ImageLoader的配置参数,并进行初始化:

  public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
//创建默认的ImageLoade配置参数
ImageLoaderConfiguration configuration = ImageLoaderConfiguration.createDefault(this);
//初始化ImageLoade参数
ImageLoader.getInstance().init(configuration);
}
}

ImageLoaderConfiguration是ImageLoader的配置参数,我们可以由代码看出其使用了单例模式和建造者模式,这里直接用createDefault()方法直接创建一个默认的ImageLoaderConfiguration,当然我们也可以直接设置自己的ImageLoaderConfiguration,设置如下

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();

这里是所有的选项参数,我们在项目中根据自己需求去设置即可,一般来说,使用默认的createDefault()方法创建configuration创建即可。之后调用ImageLoader的init()方法将ImageLoaderConfiguration参数传递进去。

进行Android Manifest配置

<manifest>
<uses-permission android:name="android.permission.INTERNET" />
<!-- Include next permission if you want to allow UIL to cache imageson SD card -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
...
<application android:name="MyApplication">
...
</application>
</manifest>

接下来我们可以进行加载图片了,首先我们可以定义好Activity的布局文件

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"> <ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/ic_picture" />
</FrameLayout>

里面只包含了一个ImageView控件,接下来我们去加载图片,我们可以观察ImageLoader项目源码可以看出,其提供了几个图片加载的方法,主要有dispalyImage()、loadImage()、loadImageSync(),其中,我们可以由方法名可以看出loadImageSync()方法是同步的,因为我们知道Android4.0版本以上有个特点,网络操作不能在主线程操作,因此,一般来说,loadImageSync()方法我们不去使用。

LoadImage()加载图片

我们可以使用ImageLoader的loadImage()方法来加载网络上的图片资源

private void initData() {

    String imgUrl = "http://img4.imgtn.bdimg.com/it/u=1326316882,880909110&fm=21&gp=0.jpg";

    ImageLoader.getInstance().loadImage(imgUrl, new ImageLoadingListener() {
@Override
public void onLoadingStarted(String s, View view) {
//开始加载图片
} @Override
public void onLoadingFailed(String s, View view, FailReason failReason) {
//图片加载失败
} @Override
public void onLoadingComplete(String s, View view, Bitmap bitmap) {
//图片加载完成
} @Override
public void onLoadingCancelled(String s, View view) {
//图片加载取消
}
});
}

传入图片的Url地址和监听图片加载情况的ImageLoaderListener监听器,在回调方法的OnLoadingComplete()中将LoadImage()设置到所在的ImageView控件上就行了,如果你觉得传入的监听器中要实现的方法太多,那么也可以选择SimpleImageLoadingListener类,此类提供了ImageLoadingListener类中方法的实现,我们可根据需要选择实现的方法即可。

ImageLoader.getInstance().loadImage(imgUrl,new SimpleImageLoadingListener(){
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
super.onLoadingComplete(imageUri, view, loadedImage);
}
});

如果我们要指定图片的大小,则就应该在开始加载图片前指定一个ImageSize对象,并指定其宽高,之后再传入给loadImage()即可。

ImageSize mImageSize = new ImageSize(100,100);
ImageLoader.getInstance().loadImage(imgUrl,mImageSize,new SimpleImageLoadingListener(){
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
super.onLoadingComplete(imageUri, view, loadedImage);
}
});

上面只是简单的加载网络图片,但在实际的开发中,因为涉及到是否需要使用内存缓存、是否使用文件缓存等等。这时我们就会用到DisplayImageOptions()来配置这些相关图片的选项。具体如下

DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.ic_loading) // resource or drawable
.showImageForEmptyUri(R.drawable.ic_empty) // resource or drawable
.showImageOnFail(R.drawable.ic_fail) // 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();

因此我们可以进行进一步对图片进行配置

private void initData() {

    String imgUrl = "http://img4.imgtn.bdimg.com/it/u=1326316882,880909110&fm=21&gp=0.jpg";

    final ImageSize mImageSize = new ImageSize(100, 100);

    DisplayImageOptions options = new DisplayImageOptions.Builder()
.cacheInMemory(true)
.cacheOnDisk(true)
.bitmapConfig(Bitmap.Config.RGB_565)
.build();
ImageLoader.getInstance().loadImage(imgUrl, mImageSize,options, new SimpleImageLoadingListener(){
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
super.onLoadingComplete(imageUri, view, loadedImage);
mImageView.setImageBitmap(loadedImage);
}
});
}

我们使用了DisplayImageOptions()来配置图片的一些选项,上面的代码将图片进行内存缓存、硬盘缓存,这样我们就不用每次加载图片就从网络上加载。但是一些选项对于loadImage()方法是无效的,如showImageOnLoading()、showImageForEmptyUri()等。

DisplayImage()加载图片

我们也可以选用DisplayImage()方法来加载网络图片

DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.ic_loading)
.showImageOnFail(R.drawable.ic_fail)
.cacheInMemory(true)
.cacheOnDisk(true)
.bitmapConfig(Bitmap.Config.RGB_565)
.build(); ImageLoader.getInstance().displayImage(imgUrl, mImageView, options);

从上面可以看出,我们利用DisplayImageOptions()方法来加载图片更加方便快捷,省去了ImageLoadingListener接口的监听,也无需手动设置显示Bitmap对象,我们直接将ImageView对象作为参数传入到displayImage()即可,在配置的参数options中,我们设置了正在加载时显示的图片,以及图片加载错误时显示的图片。

我们在加载图片的过程中,我们有需要显示图片下载进度的需求,Universal-Image-Loader当然也有这样子的功能,我们只用在displayImage()方法中传入ImageLoadingProcessListener接口,如下所示:

ImageLoader.getInstance().displayImage(imgUrl, mImageView, options, new SimpleImageLoadingListener(), new ImageLoadingProgressListener() {
@Override
public void onProgressUpdate(String imageUrl, View view, int current, int total) {
//得到图片的加载进度并进行更新
}
});

加载其他来源的图片

使用此项目框架不仅我们可以加载网络图片,还可以加载sd卡中的图片,ContentProvider等。使用时只需要将图片的地址Url修改一下就可以了。比如下面是加载文件系统上的图片代码

DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.ic_loading)
.showImageOnFail(R.drawable.ic_fail)
.cacheInMemory(true)
.cacheOnDisk(true)
.bitmapConfig(Bitmap.Config.RGB_565)
.build();
String imagePath = "/mnt/sdcard/image.png";
String imagUrl = ImageDownloader.Scheme.FILE.wrap(imagePath);
ImageLoader.getInstance().displayImage(imagUrl, mImageView, options);

对于不同的图片来源,我们只要在每个图片来源的地方加上Scheme包裹起来(Content provider)除外,然后当作图片的Url传给imageLoader中,之后项目框架就会根据不同的Scheme来获取相关的输入流

//图片来源于Content Provider
String contentProviderUrl = "content://media/external/audio/albumart/14"; //图片来源于assets资源文件夹
String assetsUrl = Scheme.ASSENTS.wrap("image.png"); //图片来源于drawable文件夹
String drawableUrl = Scheme.DRAWABLE.wrap("R.drawable.image");

ListView、GridView加载图片

我们一般情况下要展示大量的图片时都是采用GridView、ListView组件,而我们在这些组件上滚动时,我们可以停止图片的加载,而当停止滑动时,我们就可以加载当前界面上的图片。这个框架也提供这样的功能,使用起来也很简单,这提供了PauseOnScrollListener这个类来控制ListView或者GridView滑动过程中停止加载图片。

listView.setOnScrollListener(new PauseOnScrollListener(imageLoader, pauseOnScroll, pauseOnFling));
gridView.setOnScrollListener(new PauseOnScrollListener(imageLoader, pauseOnScroll, pauseOnFling));

其中第一个参数就是ImageLoader对象,第二个参数是控制是否在滑动过程中暂停加载图片,如果需要暂停则传入true,第三个参数是控制快速滑动界面时是否加载图片。

OutOfMemoryError

这个框架有很好的缓存机制,能够有效的避免OOM现象的产生,在这个框架中,对于OutOfMemoryError只是做了简单的捕获catch,从而保证我们的程序能够避免遇到OOM现象而不被crash掉,如果使用该框架经常发生OOM,则我们可以根据下面的配置进行改善。

  • 尽量减少线程池中的个数,可以在初始化的ImageLoaderConfiguration中的threadPoolSize中配置,一般是1-5线程
  • 减少图片内存消耗,在DisplayImageOptions选项中配置bitmapConfig为Bitmap.Config.RGB_565,默认是ARGB_8888,这样内存至少减少一半
  • 可使用软引用,在ImageLoaderConfiguration中配置图片的内存缓存为memoryCache(new WeakMemoryCache())或者不使用内存缓存
  • 设置图片的大小,可在DisplayImageOptions选项中设置.imageScaleType(ImageScaleType.IN_SAMPLE_INT)或者imageScaleType(imageScaleType.EXACTLY)

总结

通过上面的对Universal-Image-Loader框架的学习,相信对于一些简单的运用也已经来说比较了解,在使用框架的时候尽量用displayImage()方法去加载图片,loadImage()是将图片对象进行回调到ImageLoadingListener接口的onLoadingComplete()方法,从而将图片资源设置到ImageView控件上。如果我们需要裁剪图片时,可以向LoadImage()方法传递一个ImageSize对象,而displayImage()则会根据ImageView控件设置的测量值来裁剪图片,其次,displayImage()方法中对ImageView对象使用的是Weak references,方便垃圾回收器回收。

今天的分享就到这里,接下来我会针对此框架讲解图片的缓存策略,希望各位继续关注。

Universal-Image-Loader完全解析(上)的更多相关文章

  1. 【Android应用开发】 Universal Image Loader ( 使用简介 | 示例代码解析 )

    作者 : 韩曙亮 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/50824912 相关地址介绍 : -- Universal I ...

  2. android universal image loader 缓冲原理详解

    1. 功能介绍 1.1 Android Universal Image Loader Android Universal Image Loader 是一个强大的.可高度定制的图片缓存,本文简称为UIL ...

  3. 开源项目Universal Image Loader for Android 说明文档 (1) 简介

     When developing applications for Android, one often facesthe problem of displaying some graphical ...

  4. 开源项目Universal Image Loader for Android 说明文档 (1) 简单介绍

     When developing applications for Android, one often facesthe problem of displaying some graphical ...

  5. 【译】UNIVERSAL IMAGE LOADER. PART 3---ImageLoader详解

    在之前的文章,我们重点讲了Android-Universal-Image-Loader的三个主要组件,现在我们终于可以开始使用它了. Android-Universal-Image-Loader有四个 ...

  6. Android中Universal Image Loader开源框架的简单使用

    UIL (Universal Image Loader)aims to provide a powerful, flexible and highly customizable instrument ...

  7. universal image loader自己使用的一些感受

    1.全局入口的Application定义初始化: ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Build ...

  8. universal image loader在listview/gridview中滚动时重复加载图片的问题及解决方法

    在listview/gridview中使用UIL来display每个item的图片,当图片数量较多需要滑动滚动时会出现卡顿,而且加载过的图片再次上翻后依然会重复加载(显示设置好的加载中图片) 最近在使 ...

  9. js上传文件带参数,并且,返回给前台文件路径,解析上传的xml文件,存储到数据库中

    ajaxfileupload.js jQuery.extend({ createUploadIframe: function(id, uri) { //create frame var frameId ...

  10. android多线程-AsyncTask之工作原理深入解析(上)

    关联文章: Android 多线程之HandlerThread 完全详解 Android 多线程之IntentService 完全详解 android多线程-AsyncTask之工作原理深入解析(上) ...

随机推荐

  1. 让我们一起Go(九)

    前言: 又好久么更新了,无奈公司项目多,自己又接了私活,于是时间更不够了......不过我是不会让它流产的,坚持! 一.Go语言中的函数 终于轮到函数了,其实也没有什么好说的,无非就是一个语法问题,c ...

  2. 优化LibreOffice如此简单

    对于开源软件的支持者和粉丝来说,LibreOffice 无疑是 Microsoft Office 的最佳替代品,而且它已在过去的许多版本迭代中迎来了许多巨大改进.然而,通过用户的手动配置,我们还是有办 ...

  3. WWDC2015 结束.新一波更新以及bug即将来袭.

    WWDC结束.新一波更新以及bug即将来袭. HTTPS 将成为标准链接. http被报错. GamePlayKit 这是搞那样. 还有ReplayKit  那些什么录像分享什么的还有活路么? Mod ...

  4. int.class 与 Integer.class

    TYPE 表示的引用类型所对应的基本类型的Class对象!

  5. HMM 自学教程(一)引言

    本系列文章摘自 52nlp(我爱自然语言处理: http://www.52nlp.cn/),原文链接在 HMM 学习最佳范例,这是针对 国外网站上一个 HMM 教程 的翻译,作者功底很深,翻译得很精彩 ...

  6. Robot Framework自动化测试(二)---元素定位

    说明: 不要误认为Robot framework 只是个web UI测试工具,更正确的理解Robot framework是个测试框架,之所以可以拿来做web UI层的自动化是国为我们加入了seleni ...

  7. 使用 GistBox 轻松组织和管理你的代码片段

    GistBox 用简便的方式来组织和管理代码片段.你的代码会保存到云端进行备份,再也不用担心迷失在杂乱的代码片段中.GistBox 是建立在标准的 HTML5 技术基础上.在旅途中或在办公室,你都可以 ...

  8. 1 Servlet开篇准备

    作者:禅楼望月(http://www.cnblogs.com/yaoyinglong) 1. HTTP协议 HTTP协议是TCP/IP协议的上层协议.TCP负责确保从一个网络节点向另一个网络节点发送的 ...

  9. 新开博客 http://wylhyz.github.io/

    刚刚使用hexo在github pages上建立了静态博客,地址 http://wylhyz.github.io/

  10. 哀悼我的第一次"创业"经历

    下周考完最后一科,大学四年时光基本上算是落下帷幕,剩下的就只是整整毕业设计了.如果按照正常的节奏,这个学期应该能够搞完毕业设计的2/3,但无奈还在广州的一家公司里面实习,没有多少时间弄,得拖到3月了. ...