Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 淺析Android 開源框架ImageLoader的用法

淺析Android 開源框架ImageLoader的用法

編輯:關於Android編程

一、前言
在Android開發中,會經常涉及到顯示圖片的相關操作,在網上查閱資料,ImageLoader得到大家廣泛的使用,本篇文章針對初使用者的一個向導,同時也是自己使用該框架的一個總結,主要包含:

   ## 源碼淺析 ##
   ## 使用教程 ##
   ## 用法總結及demo下載 ##

二、源碼淺析
從用法來看,我們在使用該框架的時候,會先做一個初始化操作(一般在Application中),

ImageLoader imageLoader = ImageLoader.getInstance();
imageLoader.init(ImageLoaderConfiguration imageLoaderConfiguration );

我們在源碼進入該方法查看:

private volatile static ImageLoader instance;

    /** Returns singleton class instance */
    public static ImageLoader getInstance() {
        if (instance == null) {
            synchronized (ImageLoader.class) {
                if (instance == null) {
                    instance = new ImageLoader();
                }
            }
        }
        return instance;
    }

    protected ImageLoader() {
    }

    /**
     * Initializes ImageLoader instance with configuration.

     * If configurations was set before ( {@link #isInited()} == true) then this method does nothing.

     * To force initialization with new configuration you should {@linkplain #destroy() destroy ImageLoader} at first.
     *
     * @param configuration {@linkplain ImageLoaderConfiguration ImageLoader configuration}
     * @throws IllegalArgumentException if configuration parameter is null
     */
    public synchronized void init(ImageLoaderConfiguration configuration) {
        if (configuration == null) {
            throw new IllegalArgumentException(ERROR_INIT_CONFIG_WITH_NULL);
        }
        if (this.configuration == null) {
            L.d(LOG_INIT_CONFIG);
            engine = new ImageLoaderEngine(configuration);
            this.configuration = configuration;
        } else {
            L.w(WARNING_RE_INIT_CONFIG);
        }
    }

獲取實例對象的方式用了單例模式,這裡我們主要看一下init()這個方法:
根據注釋的文檔可知初始化時的一些注意事項,該方法主要對engine做初始化:

engine = new ImageLoaderEngine(configuration);

我們繼續進入ImageLoaderEngine中看下這個類的構造方法:

ImageLoaderEngine(ImageLoaderConfiguration configuration) {
        this.configuration = configuration;

        taskExecutor = configuration.taskExecutor;
        taskExecutorForCachedImages = configuration.taskExecutorForCachedImages;

        taskDistributor = DefaultConfigurationFactory.createTaskDistributor();
    }

可知,該庫是通過Executor對象將線程放入線程池中運行的,此構造方法裡面初始化了taskExecutorForCachedImages、taskExecutor、taskDistributor這三個對象,它們都是Executor接口的實例。關於Executor接口,

關於初始化的操作我們先看到這裡,主要是明白了它是通過Executor來處理任務的。

接下來看下顯示圖片的操作,同樣,我們看下源碼:

/**
     * Adds display image task to execution pool. Image will be set to ImageAware when it's turn.

     * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
     *
     * @param uri              Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
     * @param imageAware       {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view}
     *                         which should display image
     * @param options          {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
     *                         decoding and displaying. If null - default display image options
     *                         {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
     *                         from configuration} will be used.
     * @param listener         {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires
     *                         events on UI thread if this method is called on UI thread.
     * @param progressListener {@linkplain com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener
     *                         Listener} for image loading progress. Listener fires events on UI thread if this method
     *                         is called on UI thread. Caching on disk should be enabled in
     *                         {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions options} to make
     *                         this listener work.
     * @throws IllegalStateException    if {@link #init(ImageLoaderConfiguration)} method wasn't called before
     * @throws IllegalArgumentException if passed imageAware is null
     */
    public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
            ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
        checkConfiguration();
        if (imageAware == null) {
            throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS);
        }
        if (listener == null) {
            listener = emptyListener;
        }
        if (options == null) {
            options = configuration.defaultDisplayImageOptions;
        }

        if (TextUtils.isEmpty(uri)) {
            engine.cancelDisplayTaskFor(imageAware);
            listener.onLoadingStarted(uri, imageAware.getWrappedView());
            if (options.shouldShowImageForEmptyUri()) {
                imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources));
            } else {
                imageAware.setImageDrawable(null);
            }
            listener.onLoadingComplete(uri, imageAware.getWrappedView(), null);
            return;
        }

        ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());
        String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);
        engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);

        listener.onLoadingStarted(uri, imageAware.getWrappedView());

        Bitmap bmp = configuration.memoryCache.get(memoryCacheKey);
        if (bmp != null && !bmp.isRecycled()) {
            L.d(LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey);

            if (options.shouldPostProcess()) {
                ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
                        options, listener, progressListener, engine.getLockForUri(uri));
                ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(engine, bmp, imageLoadingInfo,
                        defineHandler(options));
                if (options.isSyncLoading()) {
                    displayTask.run();
                } else {
                    engine.submit(displayTask);
                }
            } else {
                options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);
                listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);
            }
        } else {
            if (options.shouldShowImageOnLoading()) {
                imageAware.setImageDrawable(options.getImageOnLoading(configuration.resources));
            } else if (options.isResetViewBeforeLoading()) {
                imageAware.setImageDrawable(null);
            }

            ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
                    options, listener, progressListener, engine.getLockForUri(uri));
            LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo,
                    defineHandler(options));
            if (options.isSyncLoading()) {
                displayTask.run();
            } else {
                engine.submit(displayTask);
            }
        }
    }

可知,我們將顯示圖片的任務加入到線程池中,之後ImageAware進行工作,
這裡看下這段代碼:

if (options == null) {
            options = configuration.defaultDisplayImageOptions;
}

其實就是我們設置的圖片顯示失敗時顯示的圖片,這邊是我們設置null時,使用了默認的圖片。
回到剛才,我們發現該類讀取了很多的配置參數信息,其實是我們初始化時,配置的參數,主要是配置緩存相關信息,見第二部分,使用教程。最後我們通過ImageLoaderEngine來執行顯示圖片的任務。

engine.submit(displayTask);

submit內部方法(ImageLoaderEngine類中):

/** Submits task to execution pool */
    void submit(ProcessAndDisplayImageTask task) {
        initExecutorsIfNeed();
        taskExecutorForCachedImages.execute(task);
    }

    private void initExecutorsIfNeed() {
        if (!configuration.customExecutor && ((ExecutorService) taskExecutor).isShutdown()) {
            taskExecutor = createTaskExecutor();
        }
        if (!configuration.customExecutorForCachedImages && ((ExecutorService) taskExecutorForCachedImages)
                .isShutdown()) {
            taskExecutorForCachedImages = createTaskExecutor();
        }
    }

以上是主要的一些源碼淺析,想了解更多的可以自行翻閱源碼查看。接下來我們介紹一下簡單的使用教程。

三、使用教程

首先獲取該庫:我用的是gradle配置:

在gradle中加入:

compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.3'

Maven 配置:


    com.nostra13.universalimageloader
    universal-image-loader
    1.9.3

在AndroidManifest.xml中加入(涉及到圖片緩存讀寫路徑,訪問網絡操作):


之後我們就可以代碼編寫。
首先在activity_main中設置一個顯示圖片的控件:

  

在MainActivity中使用ImageLoader加載一張網絡圖片:

imageView = (ImageView) findViewById(R.id.user_image);
ImageLoaderUtil.init(this);
String url_image = "https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWsK1HF6hhy/it/u=3266622398,4228444443&fm=116&gp=0.jpg";
ImageLoaderUtil.displayImage(url_image,imageView,ImageLoaderUtil.getAvatarDisplayOptions());

顯示的效果圖:
正常狀態下顯示的圖片

當網絡路徑不存在圖片時(顯示預設的圖片):
路徑出錯時顯示的預設圖片

ImageLoaderUtil是我自己封裝的一個類:

主要我們得編寫init()方法:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;"> public static void init(Context context) { File cacheDir = getCacheDirectory(context); //緩存文件夾路徑 ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context) .memoryCacheExtraOptions(480, 800) // default = device screen dimensions 內存緩存文件的最大長寬 .diskCacheExtraOptions(480, 800, null) // 本地緩存的詳細信息(緩存的最大長寬),最好不要設置這個 // .taskExecutor("") // .taskExecutorForCachedImages("") .threadPoolSize(3) // default 線程池內加載的數量 .threadPriority(Thread.NORM_PRIORITY - 2) // 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) // 50 Mb sd卡(本地)緩存的最大值 .diskCacheFileCount(100) // 可以緩存的文件數量 // default為使用HASHCODE對UIL進行加密命名, 還可以用MD5(new Md5FileNameGenerator())加密 .diskCacheFileNameGenerator(new HashCodeFileNameGenerator()) .imageDownloader(new BaseImageDownloader(context)) // default .imageDecoder(new BaseImageDecoder(true)) // l .defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // default .writeDebugLogs() // 打印debug log .build(); //開始構建 ImageLoader.getInstance().init(config); }

可參照注釋對應了解參數信息。

完整的ImageLoaderUtil類:

package constraintlayout.test.test.viviant.imageloadertest.util;

import android.content.Context;
import android.util.Log;
import android.widget.ImageView;

import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiscCache;
import com.nostra13.universalimageloader.cache.disc.naming.HashCodeFileNameGenerator;
import com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
import com.nostra13.universalimageloader.core.assist.QueueProcessingType;
import com.nostra13.universalimageloader.core.decode.BaseImageDecoder;
import com.nostra13.universalimageloader.core.download.BaseImageDownloader;

import java.io.File;

import constraintlayout.test.test.viviant.imageloadertest.R;

/**
 * 作者:viviant on 2016/6/30 09:22
 * 描述:
 */
public class ImageLoaderUtil {

    private static final String PICTURE_CACHE_DIR = "picture";
    private static String TAG = "ImageLoaderUtil";

    public static void init(Context context) {
        File cacheDir = getCacheDirectory(context);  //緩存文件夾路徑

        ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
                .memoryCacheExtraOptions(480, 800) // default = device screen dimensions 內存緩存文件的最大長寬
                .diskCacheExtraOptions(480, 800, null)  // 本地緩存的詳細信息(緩存的最大長寬),最好不要設置這個
//                .taskExecutor("")
//        .taskExecutorForCachedImages("")
        .threadPoolSize(3) // default  線程池內加載的數量
                .threadPriority(Thread.NORM_PRIORITY - 2) // 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) // 50 Mb sd卡(本地)緩存的最大值
                .diskCacheFileCount(100)  // 可以緩存的文件數量
                // default為使用HASHCODE對UIL進行加密命名, 還可以用MD5(new Md5FileNameGenerator())加密
                .diskCacheFileNameGenerator(new HashCodeFileNameGenerator())
                .imageDownloader(new BaseImageDownloader(context)) // default
                .imageDecoder(new BaseImageDecoder(true)) // l
                .defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // default
                .writeDebugLogs() // 打印debug log
                .build(); //開始構建
        ImageLoader.getInstance().init(config);
    }

    /**
     * 獲取緩存文件
     *
     * @param context
     * @return
     */
    public final static File getCacheDirectory(Context context) {
        String cacheDir = SystemUtility.getAppCachePath();
        return createDir(cacheDir + PICTURE_CACHE_DIR);
    }

    private final static File createDir(String dir) {
        File appCacheDir = new File(dir);
        if (!appCacheDir.exists()) {
            if (!appCacheDir.mkdirs()) {
                Log.i(TAG, "createDir# Unable to create external cache directory");
                return null;
            }
        }
        return appCacheDir;
    }

    /**
     *顯示出錯,替換的圖片
     * @return
     */
    public static DisplayImageOptions getAvatarDisplayOptions() {
        DisplayImageOptions avatarOptions = new DisplayImageOptions.Builder()
                .showImageOnLoading(R.drawable.error)
                .showImageForEmptyUri(R.drawable.error)
                .showImageOnFail(R.drawable.error)
                .cacheInMemory(true).cacheOnDisk(true).build();
        return avatarOptions;
    }

    /**
     * 顯示圖片
     *
     * @param url
     * @param imageView
     * @param options
     */
    public static void displayImage(String url, ImageView imageView,
                                    DisplayImageOptions options) {
        ImageLoader.getInstance().displayImage(url, imageView, options);
    }

}

四、用法總結及源碼下載:

ImageLoader的用法總的來說還是很便捷的,我們可以設置相應的參數來初始化配置,並且它的優點是應用進行大量的訪問網絡圖片。以上是我對該框架的一些使用方法進行總結,不足之處,歡迎批評。

源碼下載(AndroidStudio直接導入):
https://github.com/viviant1224/ImageLoaderTest

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved