android 异步图片加载组件ImageLoader封装使用

一、概述

现在开发界的图片加载使用框架真是越来越丰富了,例如ImageLoader、Glide、Picasso、Fresco等,选择非常多,而且都还不错。当然对于那些患有严重的“选择恐惧症”的人来说,到底用那个是一个相当头疼的问题。为了减轻大家的选择痛苦,我今天就选这里面的其中一个ImageLoader讲,如果大家觉得我封装的这个很好用,那就用它吧。若不好的话,那我下次换一个讲。
或许会有人问我,“这四个你为什么会选择讲ImageLoader而不是其它的呢?我来谈谈我选ImageLoader的理由
1、支持多线程下载图片,图片可以来源于网络,文件系统,项目文件夹assets中以及drawable中等
2、高度的可定制性,支持随意的配置ImageLoader,例如线程池,图片下载器,内存缓存策略,硬盘缓存策略,图片显示选项以及其他的一些配置
3、强大的图片缓存机制,支持图片的内存缓存,文件系统缓存或者SD卡缓存
4、支持图片下载过程的监听
5、根据控件(ImageView)的大小对Bitmap进行裁剪,减少Bitmap占用过多的内存
8、我只用过ImageLoader和Picasso,其它的没有用过(这个理由说的我好尴尬、心虚尴尬

二、开发实战

前面说了点废话,接下来进入正题。我们接下来的开发实战分两步走,首先是搭建ImageLoader使用环境,然后就是封装ImageLoader。

1、搭建ImageLoader使用环境

这一步很好实现,就是在build.gradle文件中添加一句话。注意,这里大家最好下载最新的。
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'

还有就是要在manifest文件中添加两个权限,分别是网络访问权限和写文件权限,毕竟人家是可以加载网络图片且需要缓存的。

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

2、封装ImageLoader

先看一下我画的封装思路图



对着这个思路图咱们一步一步的实现,首先是默认参数配置及提供必要的参数配置。
对于自己的封装的ImageLoader类,我们可以把它设置成为单例的,例如:
public static ImageLoaderUtil getInstance(Context context) {
        if (mInstance == null) {
            synchronized (ImageLoaderUtil.class) {
                if (mInstance == null) {
                    mInstance = new ImageLoaderUtil(context);
                }
            }
        }
        return mInstance;
    }

接下来配置ImageLoader基本的默认参数,有了这些参数配置,下载、加载大图片就基本可以避免OOM的问题了

    /**
     * 默认的参数值
     */
    private static final int THREAD_COUNT = 4; // 标明UIL最多可以有多少条线程
    private static final int PROPRITY = 2; // 标明图片加载的一个优先级
    private static final int DISK_CACHE_SIZE = 50 * 1024 * 1024; // 标明UIL可以最多缓存多少图片(50M)
    private static final int CONNECTION_TIME_OUT = 5 * 1000; // 连接的超时时间(5秒)
    private static final int READ_TIME_OUT = 30 * 1000; // 读取的超时时间(30秒)
上面只是设置,好么有配置。接下来看看配置,这里的配置分两个部分,一个是ImageLoaderConfiguration,另一个是DisplayImageOptions.
首先来配置ImageLoaderConfiguration,以下的配置是最基本的配置项,也是必须要的有的!注释也都很详尽,我就不罗嗦了。
   /**
     * 单例模式的私有构造方法
     *
     * @param context
     */
    private ImageLoaderUtil(Context context) {
        ImageLoaderConfiguration configuration = new ImageLoaderConfiguration
                .Builder(context)
                .threadPoolSize(THREAD_COUNT) // 配置图片下载线程的最大数量
                .threadPriority(Thread.NORM_PRIORITY - PROPRITY) // 设置优先级
                .denyCacheImageMultipleSizesInMemory() // 防止缓存多套尺寸的图片到我们的内存中
                .memoryCache(new WeakMemoryCache()) // 使用弱引用内存缓存
                .diskCacheSize(DISK_CACHE_SIZE) // 分配硬盘缓存大小
                .diskCacheFileNameGenerator(new Md5FileNameGenerator()) // 使用MD5命名文件
                .tasksProcessingOrder(QueueProcessingType.LIFO) // 图片下载顺序
                .defaultDisplayImageOptions(getDefaultOptions()) // 默认图片加载的Options
                .imageDownloader(new BaseImageDownloader(context, CONNECTION_TIME_OUT,
                        READ_TIME_OUT)) // 设置图片下载器
                .writeDebugLogs() // debug环境下会输出日志
                .build();
        // 初始化配置,创建ImageLoader实例
        ImageLoader.getInstance().init(configuration);
        mImageLoader = ImageLoader.getInstance();
    }

接下来看看DisplayImageOptions的配置。
   /**
     * 实现我们的Options
     *
     * @return
     */
    private DisplayImageOptions getDefaultOptions() {
        DisplayImageOptions options = new DisplayImageOptions.Builder()
                .showImageForEmptyUri(R.drawable.xadsdk_img_error) // 在我们图片地址为空的时候加载的默认图
                .showImageOnFail(R.drawable.xadsdk_img_error) // 图片下载失败的时候要显示的图片
                .cacheInMemory(true) // 设置图片可以缓存到内存中
                .cacheOnDisk(true) // 设置图片可以缓存到硬盘中
                .bitmapConfig(Bitmap.Config.RGB_565) // 使用的图片解码类型
                .decodingOptions(new BitmapFactory.Options()) // 图片的解码配置
                .build();
        return options;
    }

基本配置都搞定后,看看思路图的第二步,对外提供接口。在前面我在提IamgeLoader的特性时提到了支持图片下载过程的监听,这是个什么监听?给大家看看源码就明白了
public interface ImageLoadingListener {

   /**
    * Is called when image loading task was started
    *
    * @param imageUri Loading image URI
    * @param view     View for image
    */
   void onLoadingStarted(String imageUri, View view);

   /**
    * Is called when an error was occurred during image loading
    *
    * @param imageUri   Loading image URI
    * @param view       View for image. Can be <b>null</b>.
    * @param failReason {@linkplain com.nostra13.universalimageloader.core.assist.FailReason The reason} why image
    *                   loading was failed
    */
   void onLoadingFailed(String imageUri, View view, FailReason failReason);

   /**
    * Is called when image is loaded successfully (and displayed in View if one was specified)
    *
    * @param imageUri    Loaded image URI
    * @param view        View for image. Can be <b>null</b>.
    * @param loadedImage Bitmap of loaded and decoded image
    */
   void onLoadingComplete(String imageUri, View view, Bitmap loadedImage);

   /**
    * Is called when image loading task was cancelled because View for image was reused in newer task
    *
    * @param imageUri Loading image URI
    * @param view     View for image. Can be <b>null</b>.
    */
   void onLoadingCancelled(String imageUri, View view);
这个接口里声明了四个方法,分别是onLoadingStarted()下载开始,onLoadingFailed()下载失败,onLoadingComplete()下载完成,onLoadingCancelled()下载取消。因此我们只要实现了这四个方法,就可以做我们想要的操作,这里我大家看一下我的实现:
public abstract class IamgeLoaderUtilsListener implements ImageLoadingListener {
    @Override
    public void onLoadingCancelled(String imageUri, View view) {

    }

    @Override
    public void onLoadingStarted(String imageUri, View view) {

    }

    @Override
    public void onLoadingFailed(String imageUri, View view, FailReason failReason) {

    }

    @Override
    public abstract void onLoadingComplete(String imageUri, View view, Bitmap loadedImage);
}
这里我定义了一个抽象类,并实现了ImageLoadingListener接口,我在项目中只想监听图片是否完成,所以就将onLoadingComplete()设置成为抽象方法。在创建这个类时必须实现这个抽象方法,当然这个地方大家因各自的需求不同,可以自己设置。
最后就是对外提供加载图片的方法了,源码中提供了displayImage()方法,这里为了适应各种显示图片的需求,我稍微封装了一下。
   /**
     * 加载图片API,然后重载几个方法
     * @param imageView
     * @param url
     * @param options
     * @param listener
     */
    public void displayImage(ImageView imageView, String url, DisplayImageOptions options,
                             IamgeLoaderUtilsListener listener) {
        if (mImageLoader != null) {
            mImageLoader.displayImage(url,imageView,options,listener);
        }
    }

    public void displayImage(ImageView imageView, String url,IamgeLoaderUtilsListener listener){
        if (mImageLoader != null) {
            mImageLoader.displayImage(url,imageView,null,listener);
        }
    }

    public void displayImage(String url,IamgeLoaderUtilsListener listener){
        if(mImageLoader != null) {
            mImageLoader.loadImage(url, null, null, listener);
        }
    }

    public void displayImage(ImageView imageView, String url){
        if (mImageLoader != null) {
            mImageLoader.displayImage(url,imageView,null,null);
        }
    }

好了,现在我将完整的代码贴出来,让大家了解全貌
public class ImageLoaderUtil {

    /**
     * 默认的参数值
     */
    private static final int THREAD_COUNT = 4; // 标明UIL最多可以有多少条线程
    private static final int PROPRITY = 2; // 标明图片加载的一个优先级
    private static final int DISK_CACHE_SIZE = 50 * 1024 * 1024; // 标明UIL可以最多缓存多少图片(50M)
    private static final int CONNECTION_TIME_OUT = 5 * 1000; // 连接的超时时间(5秒)
    private static final int READ_TIME_OUT = 30 * 1000; // 读取的超时时间(30秒)

    private static ImageLoader mImageLoader = null;
    private static ImageLoaderUtil mInstance = null;

    public static ImageLoaderUtil getInstance(Context context) {
        if (mInstance == null) {
            synchronized (ImageLoaderUtil.class) {
                if (mInstance == null) {
                    mInstance = new ImageLoaderUtil(context);
                }
            }
        }
        return mInstance;
    }

    /**
     * 单例模式的私有构造方法
     *
     * @param context
     */
    private ImageLoaderUtil(Context context) {
        ImageLoaderConfiguration configuration = new ImageLoaderConfiguration
                .Builder(context)
                .threadPoolSize(THREAD_COUNT) // 配置图片下载线程的最大数量
                .threadPriority(Thread.NORM_PRIORITY - PROPRITY) // 设置优先级
                .denyCacheImageMultipleSizesInMemory() // 防止缓存多套尺寸的图片到我们的内存中
                .memoryCache(new WeakMemoryCache()) // 使用弱引用内存缓存
                .diskCacheSize(DISK_CACHE_SIZE) // 分配硬盘缓存大小
                .diskCacheFileNameGenerator(new Md5FileNameGenerator()) // 使用MD5命名文件
                .tasksProcessingOrder(QueueProcessingType.LIFO) // 图片下载顺序
                .defaultDisplayImageOptions(getDefaultOptions()) // 默认图片加载的Options
                .imageDownloader(new BaseImageDownloader(context, CONNECTION_TIME_OUT,
                        READ_TIME_OUT)) // 设置图片下载器
                .writeDebugLogs() // debug环境下会输出日志
                .build();
        // 初始化配置,创建ImageLoader实例
        ImageLoader.getInstance().init(configuration);
        mImageLoader = ImageLoader.getInstance();
    }

    /**
     * 实现我们的Options
     *
     * @return
     */
    private DisplayImageOptions getDefaultOptions() {
        DisplayImageOptions options = new DisplayImageOptions.Builder()
                .showImageForEmptyUri(R.drawable.xadsdk_img_error) // 在我们图片地址为空的时候加载的默认图
                .showImageOnFail(R.drawable.xadsdk_img_error) // 图片下载失败的时候要显示的图片
                .cacheInMemory(true) // 设置图片可以缓存到内存中
                .cacheOnDisk(true) // 设置图片可以缓存到硬盘中
                .bitmapConfig(Bitmap.Config.RGB_565) // 使用的图片解码类型
                .decodingOptions(new BitmapFactory.Options()) // 图片的解码配置
                .build();
        return options;
    }

    /**
     * 加载图片API,然后重载几个方法
     * @param imageView
     * @param url
     * @param options
     * @param listener
     */
    public void displayImage(ImageView imageView, String url, DisplayImageOptions options,
                             IamgeLoaderUtilsListener listener) {
        if (mImageLoader != null) {
            mImageLoader.displayImage(url,imageView,options,listener);
        }
    }

    public void displayImage(ImageView imageView, String url,IamgeLoaderUtilsListener listener){
        if (mImageLoader != null) {
            mImageLoader.displayImage(url,imageView,null,listener);
        }
    }

    public void displayImage(String url,IamgeLoaderUtilsListener listener){
        if(mImageLoader != null) {
            mImageLoader.loadImage(url, null, null, listener);
        }
    }

    public void displayImage(ImageView imageView, String url){
        if (mImageLoader != null) {
            mImageLoader.displayImage(url,imageView,null,null);
        }
    }

}
接下来最后一步,如何使用。非常方便,一句搞定
ImageLoaderUtil.getInstance(mContext).displayImage(imageView, imageUrl);
如果大家想要监听图片下载是否完成,可以这样
ImageLoaderUtil.getInstance(getApplicationContext())
                .displayImage(imageView,imageUrl, new IamgeLoaderUtilsListener() {
                    @Override
                    public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {

                    }
                });

好了,IamgeLoader封装就此结束。如果大家觉的我上面写的有不对的地方,欢迎大家指正,非常感谢。






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章