1. 程式人生 > >史上最全Universal-Image-Loader原始碼解析————核心程式碼篇

史上最全Universal-Image-Loader原始碼解析————核心程式碼篇

背景

接著上一篇的內容,我們接著看ImageLoader的核心程式碼篇,上一篇主要是看ImageLoader的記憶體優化,主要包括磁碟快取和記憶體快取,還有就是記憶體的快取策略,這一篇,我們重點來看一下ImageLoader是如何使用這些記憶體和進行圖片的展示和圖片處理和下載的。

一、code組織框架

首先我們先看code包下面的組織框架,然後我們再每個類去分析,最後我們全部串起來學習。核心程式碼篇和上一篇快取篇不一樣,核心程式碼量較多,但是我們部落格篇幅有限,我們只能按重要程度,將大部分進行講解,不多說先上截圖。

這裡寫圖片描述

二 、詳細程式碼及類講解

(1)ImageLoader

這個類可能是開發者最先接觸或者說印象最深刻的類了,我們來看一下我們是怎麼接觸它的,我們來看一下它的最簡單使用和原始碼的實現。

   //建立預設的ImageLoader配置引數
         ImageLoaderConfiguration configuration = ImageLoaderConfiguration
                .createDefault(this);
     ImageLoader.getInstance().init(configuration);
      ImageLoader.getInstance().displayImage
(imageUrl, mImageView, options);
/**
 * ImageLoader是核心主類之一
 */
public class ImageLoader {
    public static final String TAG = ImageLoader.class.getSimpleName();
    static final String LOG_INIT_CONFIG = "Initialize ImageLoader with configuration";
    static final String LOG_DESTROY = "Destroy ImageLoader"
; static final String LOG_LOAD_IMAGE_FROM_MEMORY_CACHE = "Load image from memory cache [%s]"; private static final String WARNING_RE_INIT_CONFIG = "Try to initialize ImageLoader which had already been initialized before. " + "To re-init ImageLoader with new configuration call ImageLoader.destroy() at first."; private static final String ERROR_WRONG_ARGUMENTS = "Wrong arguments were passed to displayImage() method (ImageView reference must not be null)"; private static final String ERROR_NOT_INIT = "ImageLoader must be init with configuration before using"; private static final String ERROR_INIT_CONFIG_WITH_NULL = "ImageLoader configuration can not be initialized with null"; private ImageLoaderConfiguration configuration;//引數 private ImageLoaderEngine engine;//核心 //預設是使用簡單的監聽器 private ImageLoadingListener defaultListener = new SimpleImageLoadingListener(); private volatile static ImageLoader instance; /** * 先來個單例模式 */ public static ImageLoader getInstance() { if (instance == null) { synchronized (ImageLoader.class) { if (instance == null) { instance = new ImageLoader(); } } } return instance; } protected ImageLoader() { } /** * 使用者先配置引數然後初始化進來 */ public synchronized void init(ImageLoaderConfiguration configuration) { if (configuration == null) { throw new IllegalArgumentException(ERROR_INIT_CONFIG_WITH_NULL); } if (this.configuration == null) { engine = new ImageLoaderEngine(configuration); this.configuration = configuration; } else { } } /** * 判斷是否被初始化 */ public boolean isInited() { return configuration != null; } /** * 多級呼叫 將imageview轉化為imageAware */ public void displayImage(String uri, ImageAware imageAware) { displayImage(uri, imageAware, null, null, null); } /** * 多級呼叫加監聽 */ public void displayImage(String uri, ImageAware imageAware, ImageLoadingListener listener) { displayImage(uri, imageAware, null, listener, null); } /** * 呼叫加展示引數 */ public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options) { displayImage(uri, imageAware, options, null, null); } /** * 進行多級呼叫 兩者都有 */ public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, ImageLoadingListener listener) { displayImage(uri, imageAware, options, listener, null); } /** * 這個引數有加了引數和監聽器和進度條監聽器 */ public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) { displayImage(uri, imageAware, options, null, listener, progressListener); } /** * 所有方法最終走到這裡 */ public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, ImageSize targetSize, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) { checkConfiguration();//檢查 是否有配置檔案 if (imageAware == null) {//都是判斷了 throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS); } if (listener == null) { listener = defaultListener; } if (options == null) { options = configuration.defaultDisplayImageOptions; } if (TextUtils.isEmpty(uri)) {//第一行 判斷uri是否是空 如果是空 直接取消engine中的任務 engine.cancelDisplayTaskFor(imageAware); listener.onLoadingStarted(uri, imageAware.getWrappedView());//然後呼叫listener的start if (options.shouldShowImageForEmptyUri()) {//由於 uri為空 如果設定了需要設定空的影象那麼直接設定 影象是 空的時候需要設定的影象即可 如果沒設定,直接不顯示就好 imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources)); } else { imageAware.setImageDrawable(null); }//之後呼叫 complete 回撥 返回 這是uri為空的情況 不需要做太多操作 也不需要快取 listener.onLoadingComplete(uri, imageAware.getWrappedView(), null); return; } if (targetSize == null) {//像的大小 設定是空 那麼根據控制元件設定的大小 設定 要展示圖片的大小 targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize()); } //之後 根據 uri和目標的大小 生成一個key 並把 這個任務放入 engine 的集合中 // 回撥 started方法 String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize); engine.prepareDisplayTaskFor(imageAware, memoryCacheKey); listener.onLoadingStarted(uri, imageAware.getWrappedView()); // 從記憶體快取中根據key取bitmap Bitmap bmp = configuration.memoryCache.get(memoryCacheKey); //如果存在 並且沒被回收 if (bmp != null && !bmp.isRecycled()) { //如果設定了 postProcess 執行 預設沒設定 設定這個可以提前對圖片進行某些處理 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 { //不需要 在展示圖片之前處理圖片時,那麼就直接使用 displaywe 對 圖片進行 展示 並回調complete函式 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 和 任務LoadAndDisplayImageTask 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); } } } /** * 使用者直接在外面呼叫的,裡面將他轉成了ImageViewAware */ public void displayImage(String uri, ImageView imageView) { displayImage(uri, new ImageViewAware(imageView), null, null, null); } /** * 展示圖片新增圖片大小 */ public void displayImage(String uri, ImageView imageView, ImageSize targetImageSize) { displayImage(uri, new ImageViewAware(imageView), null, targetImageSize, null, null); } /** * 展示圖片加引數 */ public void displayImage(String uri, ImageView imageView, DisplayImageOptions options) { displayImage(uri, new ImageViewAware(imageView), options, null, null); } /** * 圖片加監聽 */ public void displayImage(String uri, ImageView imageView, ImageLoadingListener listener) { displayImage(uri, new ImageViewAware(imageView), null, listener, null); } public void displayImage(String uri, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener) { displayImage(uri, imageView, options, listener, null); } /** * 展示圖片加監聽加進度條 */ public void displayImage(String uri, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) { displayImage(uri, new ImageViewAware(imageView), options, listener, progressListener); } /** * 展示圖片加監聽 */ public void loadImage(String uri, ImageLoadingListener listener) { loadImage(uri, null, null, listener, null); } /** * 展示圖片加記憶體限制加監聽 */ public void loadImage(String uri, ImageSize targetImageSize, ImageLoadingListener listener) { loadImage(uri, targetImageSize, null, listener, null); } /** */ public void loadImage(String uri, DisplayImageOptions options, ImageLoadingListener listener) { loadImage(uri, null, options, listener, null); } /** * 展示圖片加目標大小 */ public void loadImage(String uri, ImageSize targetImageSize, DisplayImageOptions options, ImageLoadingListener listener) { loadImage(uri, targetImageSize, options, listener, null); } /** * 展示圖片 所有引數都加 */ public void loadImage(String uri, ImageSize targetImageSize, DisplayImageOptions options, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) { checkConfiguration(); if (targetImageSize == null) { targetImageSize = configuration.getMaxImageSize(); } if (options == null) { options = configuration.defaultDisplayImageOptions; } NonViewAware imageAware = new NonViewAware(uri, targetImageSize, ViewScaleType.CROP); displayImage(uri, imageAware, options, listener, progressListener); } /** * 載入圖片 非同步的 */ public Bitmap loadImageSync(String uri) { return loadImageSync(uri, null, null); } /** * 載入圖片非同步加引數 */ public Bitmap loadImageSync(String uri, DisplayImageOptions options) { return loadImageSync(uri, null, options); } /** * 載入圖片限制圖片大小 */ public Bitmap loadImageSync(String uri, ImageSize targetImageSize) { return loadImageSync(uri, targetImageSize, null); } /** * 載入圖片非同步 */ public Bitmap loadImageSync(String uri, ImageSize targetImageSize, DisplayImageOptions options) { if (options == null) { options = configuration.defaultDisplayImageOptions; } options = new DisplayImageOptions.Builder().cloneFrom(options).syncLoading(true).build(); SyncImageLoadingListener listener = new SyncImageLoadingListener(); loadImage(uri, targetImageSize, options, listener); return listener.getLoadedBitmap(); } /** * Checks if ImageLoader's configuration was initialized * 檢查配置 */ private void checkConfiguration() { if (configuration == null) { throw new IllegalStateException(ERROR_NOT_INIT); } } /** * 設定監聽器. */ public void setDefaultLoadingListener(ImageLoadingListener listener) { defaultListener = listener == null ? new SimpleImageLoadingListener() : listener; } /** * Returns memory cache * 返回記憶體策略 * * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before */ public MemoryCache getMemoryCache() { checkConfiguration(); return configuration.memoryCache; } /** * Clears memory cache * * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before */ public void clearMemoryCache() { checkConfiguration(); configuration.memoryCache.clear(); } /** * Returns disk cache * 返回磁碟快取 * * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before * @deprecated Use {@link #getDiskCache()} instead */ @Deprecated public DiskCache getDiscCache() { return getDiskCache(); } /** * Returns disk cache * * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before */ public DiskCache getDiskCache() { checkConfiguration(); return configuration.diskCache; } /** * Clears disk cache. */ @Deprecated public void clearDiscCache() { clearDiskCache(); } /** * Clears disk cache. */ public void clearDiskCache() { checkConfiguration(); configuration.diskCache.clear(); } /** * Returns URI of image which is loading at this moment into passed */ public String getLoadingUriForView(ImageAware imageAware) { return engine.getLoadingUriForView(imageAware); } /** * Returns URI of image which is loading at this moment into passed */ public String getLoadingUriForView(ImageView imageView) { return engine.getLoadingUriForView(new ImageViewAware(imageView)); } /** * Cancel the task of loading and displaying image for passed * which display task will be cancelled */ public void cancelDisplayTask(ImageAware imageAware) { engine.cancelDisplayTaskFor(imageAware); } /** */ public void cancelDisplayTask(ImageView imageView) { engine.cancelDisplayTaskFor(new ImageViewAware(imageView)); } /** * 拒絕網路下載 */ public void denyNetworkDownloads(boolean denyNetworkDownloads) { engine.denyNetworkDownloads(denyNetworkDownloads); } /** * 處理網路慢 * - otherwise. */ public void handleSlowNetwork(boolean handleSlowNetwork) { engine.handleSlowNetwork(handleSlowNetwork); } /** */ public void pause() { engine.pause(); } /** * */ public void resume() { engine.resume(); } /** */ public void stop() { engine.stop(); } /** * 銷燬 */ public void destroy() { stop(); configuration.diskCache.close(); engine = null; configuration = null; } private static Handler defineHandler(DisplayImageOptions options) { Handler handler = options.getHandler(); if (options.isSyncLoading()) { handler = null; } else if (handler == null && Looper.myLooper() == Looper.getMainLooper()) { handler = new Handler(); } return handler; } /** * Listener which is designed for synchronous image loading. */ private static class SyncImageLoadingListener extends SimpleImageLoadingListener { private Bitmap loadedImage; @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { this.loadedImage = loadedImage; } public Bitmap getLoadedBitmap() { return loadedImage; } } }

(2)DefaultConfigurationFactory

這個類是一個使用工廠模式方法提供一個隊imageloader的引數配置,通過這個類,可以建立一系列的配置引數,通過程式碼我們可以清楚的看到,它預設使用hashcode的磁碟命名,如果磁碟沒有傳大小,那麼就會使用無限的磁碟快取策略,如果有傳大小的話,那麼就使用最近最少使用的磁碟快取策略,記憶體快取的話,如果有傳大小,那麼使用這個傳的大小,如果沒有穿引數的話,那麼就使用app最大的八分之一,使用最近最少快取策略,還有就是建立一些圖片展示器之類的。

/**
 * Factory for providing of default options for {@linkplain ImageLoaderConfiguration configuration}
 *一個工廠提供預設的操作對imageloader的引數
 * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
 * @since 1.5.6
 */
public class DefaultConfigurationFactory {
    /** 建立一個預設的執行器 */
    public static Executor createExecutor(int threadPoolSize, int threadPriority,
            QueueProcessingType tasksProcessingType) {
        boolean lifo = tasksProcessingType == QueueProcessingType.LIFO;
        BlockingQueue<Runnable> taskQueue =
                lifo ? new LIFOLinkedBlockingDeque<Runnable>() : new LinkedBlockingQueue<Runnable>();
        return new ThreadPoolExecutor(threadPoolSize, threadPoolSize, 0L, TimeUnit.MILLISECONDS, taskQueue,
                createThreadFactory(threadPriority, "uil-pool-"));
    }

    /** /預設的任務分發Executor  */
    public static Executor createTaskDistributor() {
        return Executors.newCachedThreadPool(createThreadFactory(Thread.NORM_PRIORITY, "uil-pool-d-"));
    }

    /**預設的磁碟快取檔名的生成策略   */
    public static FileNameGenerator createFileNameGenerator() {
        return new HashCodeFileNameGenerator();
    }

    /**
     * 預設的硬體快取配置  如果值為0的話就用無限的磁碟快取 如果大於0就用最近最少使用快取
     */
    public static DiskCache createDiskCache(Context context, FileNameGenerator diskCacheFileNameGenerator,
                                            long diskCacheSize, int diskCacheFileCount) {
        File reserveCacheDir = createReserveDiskCacheDir(context);
        if (diskCacheSize > 0 || diskCacheFileCount > 0) {
            File individualCacheDir = StorageUtils.getIndividualCacheDirectory(context);
            try {
                return new LruDiskCache(individualCacheDir, reserveCacheDir, diskCacheFileNameGenerator, diskCacheSize,
                        diskCacheFileCount);
            } catch (IOException e) {
                L.e(e);
                // continue and create unlimited cache
            }
        }
        File cacheDir = StorageUtils.getCacheDirectory(context);
        return new UnlimitedDiskCache(cacheDir, reserveCacheDir, diskCacheFileNameGenerator);
    }

    /** Creates reserve disk cache folder which will be used if primary disk cache folder becomes unavailable */
    private static File createReserveDiskCacheDir(Context context) {//預設的備用硬體快取目錄
        File cacheDir = StorageUtils.getCacheDirectory(context, false);
        File individualDir = new File(cacheDir, "uil-images");
        if (individualDir.exists() || individualDir.mkdir()) {
            cacheDir = individualDir;
        }
        return cacheDir;
    }

    /**
     * Creates default implementation of {@link MemoryCache} - {@link LruMemoryCache}<br />
     * Default cache size = 1/8 of available app memory.
     *
     * 配置 如果傳入的預設大小為0 那麼就拿到App最大記憶體的八分之一
     * 如果不為0 那麼就用傳入的大小來作為最近最少使用演算法的記憶體
     */
    public static MemoryCache createMemoryCache(Context context, int memoryCacheSize) {
        if (memoryCacheSize == 0) {
            ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            int memoryClass = am.getMemoryClass();
            if (hasHoneycomb() && isLargeHeap(context)) {
                memoryClass = getLargeMemoryClass(am);
            }
            memoryCacheSize = 1024 * 1024 * memoryClass / 8;
        }
        return new LruMemoryCache(memoryCacheSize);
    }

    private static boolean hasHoneycomb() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private static boolean isLargeHeap(Context context) {
        return (context.getApplicationInfo().flags & ApplicationInfo.FLAG_LARGE_HEAP) != 0;
    }
      //
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private static int getLargeMemoryClass(ActivityManager am) {
        return am.getLargeMemoryClass();
    }
     //建立一個圖片下載器
    /** Creates default implementation of {@link ImageDownloader} - {@link BaseImageDownloader} */
    public static ImageDownloader createImageDownloader(Context context) {
        return new BaseImageDownloader(context);
    }
       //建立一個圖片修改器
    /** Creates default implementation of {@link ImageDecoder} - {@link BaseImageDecoder} */
    public static ImageDecoder createImageDecoder(boolean loggingEnabled) {
        return new BaseImageDecoder(loggingEnabled);
    }
//預設的BitmapDisplayer
    /** Creates default implementation of {@link BitmapDisplayer} - {@link SimpleBitmapDisplayer} */
    public static BitmapDisplayer createBitmapDisplayer() {
        return new SimpleBitmapDisplayer();
    }
//預設的TreadFactory
    /** Creates default implementation of {@linkplain ThreadFactory thread factory} for task executor */
    private static ThreadFactory createThreadFactory(int threadPriority, String threadNamePrefix) {
        return new DefaultThreadFactory(threadPriority, threadNamePrefix);
    }

    private static class DefaultThreadFactory implements ThreadFactory {

        private static final AtomicInteger poolNumber = new AtomicInteger(1);

        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;
        private final int threadPriority;

        DefaultThreadFactory(int threadPriority, String threadNamePrefix) {
            this.threadPriority = threadPriority;
            group = Thread.currentThread().getThreadGroup();
            namePrefix = threadNamePrefix + poolNumber.getAndIncrement() + "-thread-";
        }

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
            if (t.isDaemon()) t.setDaemon(false);
            t.setPriority(threadPriority);
            return t;
        }
    }
}

(3)DisplayBitmapTask

一個顯示圖片任務的類,開了一個子執行緒實現Runnable介面,主要是run的一個判斷,註解才是重點

/**
 *顯示圖片任務的一個類
 */
final class DisplayBitmapTask implements Runnable {

    private static final String LOG_DISPLAY_IMAGE_IN_IMAGEAWARE = "Display image in ImageAware (loaded from %1$s) [%2$s]";
    private static final String LOG_TASK_CANCELLED_IMAGEAWARE_REUSED = "ImageAware is reused for another image. Task is cancelled. [%s]";
    private static final String LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED = "ImageAware was collected by GC. Task is cancelled. [%s]";

    private final Bitmap bitmap;
    private final String imageUri;//
    private final ImageAware imageAware;//
    private final String memoryCacheKey;
    private final BitmapDisplayer displayer;
    private final ImageLoadingListener listener;
    private final ImageLoaderEngine engine;
    private final LoadedFrom loadedFrom;
   //初始化
    public DisplayBitmapTask(Bitmap bitmap, ImageLoadingInfo imageLoadingInfo, ImageLoaderEngine engine,
            LoadedFrom loadedFrom) {
        this.bitmap = bitmap;
        imageUri = imageLoadingInfo.uri;
        imageAware = imageLoadingInfo.imageAware;
        memoryCacheKey = imageLoadingInfo.memoryCacheKey;
        displayer = imageLoadingInfo.options.getDisplayer();
        listener = imageLoadingInfo.listener;
        this.engine = engine;
        this.loadedFrom = loadedFrom;
    }

    @Override
    public void run() {
        if (imageAware.isCollected()) {//判斷圖片View是否已經被回收了,如果已經被回收,則取消任務
            L.d(LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED, memoryCacheKey);
            listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
        } else if (isViewWasReused()) {//圖片View是否又用來展示其他圖片了,如果是的話,則取消任務
            L.d(LOG_TASK_CANCELLED_IMAGEAWARE_REUSED, memoryCacheKey);
            listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
        } else {//展示圖片 完成介面回撥
            L.d(LOG_DISPLAY_IMAGE_IN_IMAGEAWARE, loadedFrom, memoryCacheKey);
            displayer.display(bitmap, imageAware, loadedFrom);
            engine.cancelDisplayTaskFor(imageAware);
            listener.onLoadingComplete(imageUri, imageAware.getWrappedView(), bitmap);
        }
    }

    /** 檢查imageview是否有效 */
    private boolean isViewWasReused() {
        String currentCacheKey = engine.getLoadingUriForView(imageAware);
        return !memoryCacheKey.equals(currentCacheKey);
    }
}

(4)DisplayImageOptions

展示圖片的一個引數操作,展示的時候傳入引數,展示的圖片會根據傳入的引數進行展示。

/**
 * 展示圖片的一個引數操作
 */
public final class DisplayImageOptions {
    private final int imageResOnLoading;
    private final int imageResForEmptyUri;
    private final int imageResOnFail;
    private final Drawable imageOnLoading;
    private final Drawable imageForEmptyUri;
    private final Drawable imageOnFail;
    private final boolean resetViewBeforeLoading;
    private final boolean cacheInMemory;
    private final boolean cacheOnDisk;
    private final ImageScaleType imageScaleType;
    private final Options decodingOptions;
    private final int delayBeforeLoading;
    private final boolean considerExifParams;
    private final Object extraForDownloader;
    private final BitmapProcessor preProcessor;
    private final BitmapProcessor postProcessor;
    private final BitmapDisplayer displayer;
    private final Handler handler;
    private final boolean isSyncLoading;

    private DisplayImageOptions(Builder builder) {
        imageResOnLoading = builder.imageResOnLoading;
        imageResForEmptyUri = builder.imageResForEmptyUri;
        imageResOnFail = builder.imageResOnFail;
        imageOnLoading = builder.imageOnLoading;
        imageForEmptyUri = builder.imageForEmptyUri;
        imageOnFail = builder.imageOnFail;
        resetViewBeforeLoading = builder.resetViewBeforeLoading;
        cacheInMemory = builder.cacheInMemory;
        cacheOnDisk = builder.cacheOnDisk;
        imageScaleType = builder.imageScaleType;
        decodingOptions = builder.decodingOptions;
        delayBeforeLoading = builder.delayBeforeLoading;
        considerExifParams = builder.considerExifParams;
        extraForDownloader = builder.extraForDownloader;
        preProcessor = builder.preProcessor;
        postProcessor = builder.postProcessor;
        displayer = builder.displayer;
        handler = builder.handler;
        isSyncLoading = builder.isSyncLoading;
    }
   //判斷是否在下載的時候需要顯示圖片
    public boolean shouldShowImageOnLoading() {
        return imageOnLoading != null || imageResOnLoading != 0;
    }
 //空uri的時候是否需要顯示圖片
    public boolean shouldShowImageForEmptyUri() {
        return imageForEmptyUri != null || imageResForEmptyUri != 0;
    }
 //是否需要顯示圖片在下載失敗的時候
    public boolean shouldShowImageOnFail() {
        return imageOnFail != null || imageResOnFail != 0;
    }
//是否應該提前處理圖片
    public boolean shouldPreProcess() {
        return preProcessor != null;
    }
//是否後面需要處理圖片
    public boolean shouldPostProcess() {
        return postProcessor != null;
    }
//是否延遲載入圖片
    public boolean shouldDelayBeforeLoading() {
        return delayBeforeLoading > 0;
    }
//拿到圖片
    public Drawable getImageOnLoading(Resources res) {
        return imageResOnLoading != 0 ? res.getDrawable(imageResOnLoading) : imageOnLoading;
    }
    //拿到圖片
    public Drawable getImageForEmptyUri(Resources res) {
        return imageResForEmptyUri != 0 ? res.getDrawable(imageResForEmptyUri) : imageForEmptyUri;
    }
    //拿到圖片
    public Drawable getImageOnFail(Resources res) {
        return imageResOnFail != 0 ? res.getDrawable(imageResOnFail) : imageOnFail;
    }
    //是否重置view
    public boolean isResetViewBeforeLoading() {
        return resetViewBeforeLoading;
    }
    //要不要存到記憶體中
    public boolean isCacheInMemory() {
        return cacheInMemory;
    }
//要不要存到磁碟中
    public boolean isCacheOnDisk() {
        return cacheOnDisk;
    }
//縮放型別
    public ImageScaleType getImageScaleType() {
        return imageScaleType;
    }
    //操作型別
    public Options getDecodingOptions() {
        return decodingOptions;
    }
     //延遲載入
    public int getDelayBeforeLoading() {
        return delayBeforeLoading;
    }

    public boolean isConsiderExifParams() {
        return considerExifParams;
    }
 //拿到額外新增引數
    public Object getExtraForDownloader() {
        return extraForDownloader;
    }
 //拿到提前處理
    public BitmapProcessor getPreProcessor() {
        return preProcessor;
    }
//拿到處理後
    public BitmapProcessor getPostProcessor() {
        return postProcessor;
    }
//拿到展示器
    public BitmapDisplayer getDisplayer() {
        return displayer;
    }

    public Handler getHandler() {
        return handler;
    }
    boolean isSyncLoading() {
        return isSyncLoading;
    }
    /**
     * Builder for {@link DisplayImageOptions}
     */
    public static class Builder {
        private int imageResOnLoading = 0; //在圖片下載期間顯示的圖片
        private int imageResForEmptyUri = 0;//設定圖片Uri為空或是錯誤的時候顯示的圖片
        private int imageResOnFail = 0;  //設定圖片載入/解碼過程中錯誤時候顯示的圖片
        private Drawable imageOnLoading = null;//在圖片下載期間顯示的圖片
        private Drawable imageForEmptyUri = null;//設定圖片Uri為空或是錯誤的時候顯示的圖片
        private Drawable imageOnFail = null;//設定圖片載入/解碼過程中錯誤時候顯示的圖片
        private boolean resetViewBeforeLoading = false; //設定圖片在下載前是否重置,復位
        private boolean cacheInMemory = false;//設定下載的圖片是否快取在記憶體中
        private boolean cacheOnDisk = false;  //設定下載的圖片是否快取在SD卡中
        private ImageScaleType imageScaleType = ImageScaleType.IN_SAMPLE_POWER_OF_2;
        private Options decodingOptions = new Options(); //設定圖片的解碼型別   //設定圖片的解碼配置
        private int delayBeforeLoading = 0;//設定圖片下載前的延遲
        private boolean considerExifParams = false;//設定圖片以如何的編碼方式顯示
        private Object extraForDownloader = null;//設定額外的內容給ImageDownloade
        private BitmapProcessor preProcessor = null;//設定圖片加入快取前,對bitmap進行設定
        private BitmapProcessor postProcessor = null; //設定顯示前的圖片,顯示後這個圖片一直保留在快取中
        private BitmapDisplayer displayer = DefaultConfigurationFactory.createBitmapDisplayer();
        private Handler handler = null;
        private boolean isSyncLoading = false;
        /**
         * 設定引數欄位參考上面註釋
         */
        @Deprecated
        public Builder showStubImage(int imageRes) {
            imageResOnLoading = imageRes;
            return this;
        }
        /**
         * 設定引數欄位參考上面註釋
         */
        public Builder showImageOnLoading(int imageRes) {
            imageResOnLoading = imageRes;
            return this;
        }
        /**
         * 設定引數欄位參考上面註釋
         */
        public Builder showImageOnLoading(Drawable drawable) {
            imageOnLoading = drawable;
            return this;
        }
        /**
         * 設定引數欄位參考上面註釋
         */