Android 图片加载库系列之Universal-Image-Loader

未经本人授权,不得转载!否则必将维权到底

平时做开发的时候, 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

  1. 添加依赖:compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
  2. 添加权限:

    <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>
    
  3. 在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);

  4. 显示图片的常用方法:

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);
  1. 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 :对图片缓存进行总体配置,包括内存缓存的大小,本地缓存的大小和位置、日志、下载策略(FIFO 还是 LIFO)等等。
- ImageLoader:是具体下载图片,缓存图片,显示图片的具体执行类,它有两个具体的方法displayImage()、loadImage(),但是其实最终他们的实现都是displayImage()
- DisplayImageOptions :在每个页面需要显示图片的地方,控制如何显示的细节,比如指定下载时的默认图(包括下载中、下载失败、URL为空等),是否将缓存放到内存或者本地磁盘。

从三者的协作关系上看,他们有点像厨房规定、厨师、客户个人口味之间的关系。ImageLoaderConfiguration 就像是厨房里面的规定,每一个厨师要怎么着装,要怎么保持厨房的干净,这是针对每一个厨师都适用的规定,而且不允许个性化改变。ImageLoader 就像是具体做菜的厨师,负责具体菜谱的制作。 DisplayImageOptions 就像每个客户的偏好,根据客户是重口味还是清淡,每一个 ImageLoader 根据 DisplayImageOptions 的要求具体执行。

四、ImageLoader 具体使用:

  1. 纯粹为了加载默认配置的一个图片的:
ImageLoader.getInstance().displayImage(imageUrl, imageView);
 ```
2. 加载自定义配置的一个图片的:

// imageUrl代表图片的URL地址,imageView代表承载图片的IMAGEVIEW控件 , options代表DisplayImageOptions配置文件
ImageLoader.getInstance().displayImage(imageUrl, imageView,options);
“`

  1. 加载图片时带回调监听:
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,我们应该怎么去改善呢?

  • 减少线程池中线程的个数,在 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)

六、结语:

Universal-Image-Loader 已经出现很多年了,直到现在为止还是有不少公司(比如我们公司~)出于项目的稳定性的考虑,继续使用该图片加载库,所以还是很有必要好好学习一下的。

本文参考:
Android 开源框架Universal-Image-Loader完全解析(一)基本介绍及使用
Android-Universal-Image-Loader三大组件DisplayImageOptions、ImageLoader、ImageLoaderConfiguration详解
Android-Universal-Image-Loader 图片异步加载类库的使用(超详细配置)
推荐阅读:
Java基础 之软引用、弱引用、虚引用


本文原创发布于微信公众号「keithxiaoy」,编程、思维、成长、正能量,关注并回复「编程」、「阅读」、「Java」、「Python」等关键字获取免费学习资料

不要给自己的人生设限

猜你喜欢

转载自blog.csdn.net/xiaoy_yan/article/details/80985805