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封裝就此結束。如果大家覺的我上面寫的有不對的地方,歡迎大家指正,非常感謝。






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