Universal-Image-Loader解析(三)源代碼解析

Universal-Image-Loader解析系列

Universal-Image-Loader解析(一)基本介紹與使用
Universal-Image-Loader解析(二)內(nèi)部緩存原理
Universal-Image-Loader解析(三)源代碼解析

前兩篇文章主要跟大家介紹了UIL的基本使用以及它的緩存設計,本篇文章主要講解它的源代碼

當我們配置好ImageConfigurationImageLoader后,我們就會開始調(diào)用

ImageLoader.getInstance().loadImage(...);   
ImageLoader.getInstance().displayImage(...);

這兩個方法其中一個來顯示圖片。
先看loadImage

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);
    }

首先調(diào)用了checkConfiguration用來判斷是否有初始化ImageLoaderConfiguration
如果有設置ImageView的大小,則設置,沒則默認Configuration的大小。
如果沒有設置DisplayImageOptions,則設置上一個默認的options
之后創(chuàng)建了個NonViewAware,再調(diào)用displayImage
也就是說,loadImage最終還是調(diào)用到了displayImage。

ImageAware

這里的NonViewAware實現(xiàn)了ImageAware接口。先來看個結構圖

ImageAware

ImageAware是一個接口,內(nèi)部提供了一系列操作圖片的一些方法。
對于NonViewAware來說,它內(nèi)部只是簡單的保存圖片一些必要的數(shù)據(jù),比如圖片大小尺寸,URI,ScaleType這些。主要封裝成ImageAware來給displayImage調(diào)用。

看下displayImage的使用

public void displayImage(String uri, ImageView imageView) {
        displayImage(uri, new ImageViewAware(imageView), null, null, null);
    }

這里把ImageView封裝成ImageViewAware再去調(diào)用displayImage這個就跟loadImage一樣。
而這里ImageViewAware繼承與ViewAware,ViewAware則實現(xiàn)了ImageAware接口。
NonViewAware不同的是ViewAware內(nèi)部持有一個Reference<View> viewRef的成員變量,它是用來保存當前ImageView的一個弱引用,以便之后來直接設置顯示圖片。
ViewAware很多方法都是依賴于這個View

@Override
    public boolean setImageDrawable(Drawable drawable) {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            View view = viewRef.get();
            if (view != null) {
                setImageDrawableInto(drawable, view);
                return true;
            }
        } else {
            L.w(WARN_CANT_SET_DRAWABLE);
        }
        return false;
    }

之后就可以在ImageViewAware中設置顯示。

好了回過頭看他們最終調(diào)用的方法。
這個方法有點長,我們拆分成一部分一部分來看

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)) {
            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;
        }
        ...//下一部分看
    }

首先先檢查是否有初始化設置ImageLoaderConfiguration沒則拋出異常,沒設置listener和DisplayImageOptions則設置一個默認值。

之后調(diào)用TextUtils.isEmpty(uri)判斷是否當前的uri為空,則調(diào)用
engine.cancelDisplayTaskFor(imageAware);
之后則用listener通知開始和結束,也比較好理解,主要是這個engine。

這個engine就是ImageLoaderEngine,主要用來負責顯示加載圖片的一個類。
ImageLoaderEngine中存在一個HashMap,用來記錄正在加載的任務,加載圖片的時候會將ImageView的id和圖片的url加上尺寸加入到HashMap中,加載完成之后會將其移除。

接著看下面

public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
            ImageSize targetSize, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
        ...//前一部分
        if (targetSize == null) {
            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);
            }
        } 
        ...//下一部分
    }

當URI不為空的時候來加載顯示。首先根據(jù)uri獲取對應uri對應唯一的一個Key,之后調(diào)用engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);來記錄當前加載的任務,開啟listener的start回調(diào),接著調(diào)用Bitmap bmp = configuration.memoryCache.get(memoryCacheKey);來獲取內(nèi)存緩存中的圖片,這里默認的內(nèi)存緩存是LruMemoryCache,前篇文章有分析到。

如果緩存中存在相應的Bitmap的話,進入到if里面
我們?nèi)绻贒isplayImageOptions中設置了postProcessor就進入true邏輯,不過默認postProcessor是為null的,BitmapProcessor接口主要是對Bitmap進行處理,這個框架并沒有給出相對應的實現(xiàn),如果我們有自己的需求的時候可以自己實現(xiàn)BitmapProcessor接口(比如將圖片設置成圓形的).

然后到了27行
將Bitmap設置到ImageView上面,這里我們可以在DisplayImageOptions中配置顯示需求displayer,默認使用的是SimpleBitmapDisplayer,直接將Bitmap設置到ImageView上面,我們可以配置其他的顯示邏輯, 他這里提供了FadeInBitmapDisplayer(透明度從0-1)RoundedBitmapDisplayer(4個角是圓弧)等, 然后回調(diào)到ImageLoadingListener接口。

我們知道loadImagedisplayImage的區(qū)別在于loadImage依靠返回的Bitmap進行設置顯示,而displayImage則是直接顯示。而loadImage最終也是調(diào)用了displayImage,原因就在于這個display和imageAware

public final class SimpleBitmapDisplayer implements BitmapDisplayer {
    @Override
    public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {
        imageAware.setImageBitmap(bitmap);
    }
}

loadImageImageAwareNonImageAware并沒有處理setImageBitmap的方法,而displayImageImageViewAware則有處理顯示。

好,繼續(xù)前面,當從內(nèi)存緩存獲取到的Bitmap為空的情況下

public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
            ImageSize targetSize, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
            ...//前兩部分
        //如果Bitmap為空
        } 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);
            }
        }
    }

如果需要設置顯示加載中的圖片,則進行設置顯示。
ImageLoadingInfo則是一個加載顯示圖片任務信息的一個類。
之后根據(jù)它創(chuàng)建了一個LoadAndDisplayImageTask類,它實現(xiàn)了Runnable。
如果配置了isSyncLoading為true, 直接執(zhí)行LoadAndDisplayImageTask的run方法,表示同步,默認是false,將LoadAndDisplayImageTask提交給線程池對象

接下來我們就看LoadAndDisplayImageTask的run(), 這個類還是蠻復雜的,我們還是一段一段的分析。

@Override
    public void run() {
        if (waitIfPaused()) return;
        if (delayIfNeed()) return;

        ...
    }

如果waitIfPaused(), delayIfNeed()返回true的話,直接從run()方法中返回了,不執(zhí)行下面的邏輯, 接下來我們先看看waitIfPaused()

private boolean waitIfPaused() {
        AtomicBoolean pause = engine.getPause();
        if (pause.get()) {
            synchronized (engine.getPauseLock()) {
                if (pause.get()) {
                    L.d(LOG_WAITING_FOR_RESUME, memoryCacheKey);
                    try {
                        engine.getPauseLock().wait();
                    } catch (InterruptedException e) {
                        L.e(LOG_TASK_INTERRUPTED, memoryCacheKey);
                        return true;
                    }
                    L.d(LOG_RESUME_AFTER_PAUSE, memoryCacheKey);
                }
            }
        }
        return isTaskNotActual();
    }

這個方法是干嘛用呢,主要是我們在使用ListView,GridView去加載圖片的時候,有時候為了滑動更加的流暢,我們會選擇手指在滑動或者猛地一滑動的時候不去加載圖片,所以才提出了這么一個方法,那么要怎么用呢? 這里用到了PauseOnScrollListener這個類,使用很簡單ListView.setOnScrollListener(new PauseOnScrollListener(pauseOnScroll, pauseOnFling )), pauseOnScroll控制我們緩慢滑動ListView,GridView是否停止加載圖片,pauseOnFling 控制猛的滑動ListView,GridView是否停止加載圖片。

我們可以看下這個PauseOnScrollListener的處理

@Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        switch (scrollState) {
            case OnScrollListener.SCROLL_STATE_IDLE:
                imageLoader.resume();
                break;
            case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
                if (pauseOnScroll) {
                    imageLoader.pause();
                }
                break;
            case OnScrollListener.SCROLL_STATE_FLING:
                if (pauseOnFling) {
                    imageLoader.pause();
                }
                break;
        }
        if (externalListener != null) {
            externalListener.onScrollStateChanged(view, scrollState);
        }
    }

滑動停止的話會調(diào)用到imageLoader.pause

public void pause() {
        engine.pause();
    }
...
void pause() {
        paused.set(true);
    }
這里的pause是
private final AtomicBoolean paused = new AtomicBoolean(false);

所以調(diào)用pause.get則會返回true。

除此之外,這個方法的返回值由isTaskNotActual()決定,我們接著看看isTaskNotActual()的源碼

private boolean isTaskNotActual() {
        return isViewCollected() || isViewReused();
    }

isViewCollected()是判斷我們ImageView是否被垃圾回收器回收了,如果回收了,LoadAndDisplayImageTask方法的run()就直接返回了,isViewReused()判斷該ImageView是否被重用,被重用run()方法也直接返回,為什么要用isViewReused()方法呢?主要是ListView,GridView我們會復用item對象,假如我們先去加載ListView,GridView第一頁的圖片的時候,第一頁圖片還沒有全部加載完我們就快速的滾動,isViewReused()方法就會避免這些不可見的item去加載圖片,而直接加載當前界面的圖片。

回頭繼續(xù)看run方法

@Override
    public void run() {
        ...
        ReentrantLock loadFromUriLock = imageLoadingInfo.loadFromUriLock;
        L.d(LOG_START_DISPLAY_IMAGE_TASK, memoryCacheKey);
        if (loadFromUriLock.isLocked()) {
            L.d(LOG_WAITING_FOR_IMAGE_LOADED, memoryCacheKey);
        }

        loadFromUriLock.lock();
        Bitmap bmp;
        try {
            checkTaskNotActual();

            bmp = configuration.memoryCache.get(memoryCacheKey);
            if (bmp == null || bmp.isRecycled()) {
                bmp = tryLoadBitmap();
                if (bmp == null) return; // listener callback already was fired

                checkTaskNotActual();
                checkTaskInterrupted();

                if (options.shouldPreProcess()) {
                    L.d(LOG_PREPROCESS_IMAGE, memoryCacheKey);
                    bmp = options.getPreProcessor().process(bmp);
                    if (bmp == null) {
                        L.e(ERROR_PRE_PROCESSOR_NULL, memoryCacheKey);
                    }
                }

                if (bmp != null && options.isCacheInMemory()) {
                    L.d(LOG_CACHE_IMAGE_IN_MEMORY, memoryCacheKey);
                    configuration.memoryCache.put(memoryCacheKey, bmp);
                }
            } else {
                loadedFrom = LoadedFrom.MEMORY_CACHE;
                L.d(LOG_GET_IMAGE_FROM_MEMORY_CACHE_AFTER_WAITING, memoryCacheKey);
            }

            if (bmp != null && options.shouldPostProcess()) {
                L.d(LOG_POSTPROCESS_IMAGE, memoryCacheKey);
                bmp = options.getPostProcessor().process(bmp);
                if (bmp == null) {
                    L.e(ERROR_POST_PROCESSOR_NULL, memoryCacheKey);
                }
            }
            checkTaskNotActual();
            checkTaskInterrupted();
        } catch (TaskCancelledException e) {
            fireCancelEvent();
            return;
        } finally {
            loadFromUriLock.unlock();
        }

        DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom);
        runTask(displayBitmapTask, syncLoading, handler, engine);
    }

第4行代碼有一個loadFromUriLock,這個是一個鎖,獲取鎖的方法在ImageLoaderEngine類的getLockForUri()方法中

ReentrantLock getLockForUri(String uri) {  
        ReentrantLock lock = uriLocks.get(uri);  
        if (lock == null) {  
            lock = new ReentrantLock();  
            uriLocks.put(uri, lock);  
        }  
        return lock;  
    }  

從上面可以看出,這個鎖對象與圖片的url是相互對應的,為什么要這么做?也行你還有點不理解,不知道大家有沒有考慮過一個場景,假如在一個ListView中,某個item正在獲取圖片的過程中,而此時我們將這個item滾出界面之后又將其滾進來,滾進來之后如果沒有加鎖,該item又會去加載一次圖片,假設在很短的時間內(nèi)滾動很頻繁,那么就會出現(xiàn)多次去網(wǎng)絡上面請求圖片,所以這里根據(jù)圖片的Url去對應一個ReentrantLock對象,讓具有相同Url的請求就會在第10行等待,等到這次圖片加載完成之后,ReentrantLock就被釋放,剛剛那些相同Url的請求就會繼續(xù)執(zhí)行第10行下面的代碼。

之后來到第13行,先調(diào)用checkTaskNotActual判斷當前View是否被GC回收使用,是則拋出異常。
接著15行,它們會先從內(nèi)存緩存中獲取一遍,如果內(nèi)存緩存中沒有在去執(zhí)行下面的邏輯,所以ReentrantLock的作用就是避免這種情況下重復的去從網(wǎng)絡上面請求圖片。

17行的方法tryLoadBitmap(),這個方法確實也有點長,我先告訴大家,這里面的邏輯是先從文件緩存中獲取有沒有Bitmap對象,如果沒有在去從網(wǎng)絡中獲取,然后將bitmap保存在文件系統(tǒng)中,我們還是具體分析下

private Bitmap tryLoadBitmap() throws TaskCancelledException {
        Bitmap bitmap = null;
        try {
            File imageFile = configuration.diskCache.get(uri);
            if (imageFile != null && imageFile.exists() && imageFile.length() > 0) {
                L.d(LOG_LOAD_IMAGE_FROM_DISK_CACHE, memoryCacheKey);
                loadedFrom = LoadedFrom.DISC_CACHE;

                checkTaskNotActual();
                bitmap = decodeImage(Scheme.FILE.wrap(imageFile.getAbsolutePath()));
            }
            if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
                L.d(LOG_LOAD_IMAGE_FROM_NETWORK, memoryCacheKey);
                loadedFrom = LoadedFrom.NETWORK;

                String imageUriForDecoding = uri;
                if (options.isCacheOnDisk() && tryCacheImageOnDisk()) {
                    imageFile = configuration.diskCache.get(uri);
                    if (imageFile != null) {
                        imageUriForDecoding = Scheme.FILE.wrap(imageFile.getAbsolutePath());
                    }
                }

                checkTaskNotActual();
                bitmap = decodeImage(imageUriForDecoding);

                if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
                    fireFailEvent(FailType.DECODING_ERROR, null);
                }
            }
        }
        ...
        return bitmap;
    }

首先在第4行會去磁盤緩存中去獲取圖片,如果圖片已經(jīng)保存在磁盤了,則直接獲取對應的File路徑,調(diào)用bitmap = decodeImage(Scheme.FILE.wrap(imageFile.getAbsolutePath()));進行解析。

如果在磁盤中沒有的話,則到了12行,開始進行網(wǎng)絡下載獲取。
在17行會去調(diào)用isCacheOnDisk判斷是否要保持在磁盤中,如果默認false,如果是則調(diào)用tryCacheImageOnDisk來下載圖片并且保持在磁盤

private boolean tryCacheImageOnDisk() throws TaskCancelledException {
        L.d(LOG_CACHE_IMAGE_ON_DISK, memoryCacheKey);

        boolean loaded;
        try {
            loaded = downloadImage();
            ...
        } ...
        return loaded;
    }

調(diào)用了downloadImage進行下載圖片

private boolean downloadImage() throws IOException {
        InputStream is = getDownloader().getStream(uri, options.getExtraForDownloader());
        if (is == null) {
            L.e(ERROR_NO_IMAGE_STREAM, memoryCacheKey);
            return false;
        } else {
            try {
                return configuration.diskCache.save(uri, is, this);
            } finally {
                IoUtils.closeSilently(is);
            }
        }
    }

可以看到這里調(diào)用了getDownloader().getStream來下載,這里先不擴展,在后面會說到
下載之后則保存在磁盤中。
回來前面

String imageUriForDecoding = uri;
                if (options.isCacheOnDisk() && tryCacheImageOnDisk()) {
                    imageFile = configuration.diskCache.get(uri);
                    if (imageFile != null) {
                        imageUriForDecoding = Scheme.FILE.wrap(imageFile.getAbsolutePath());
                    }
                }
                checkTaskNotActual();
                bitmap = decodeImage(imageUriForDecoding);

這里有個String變量imageUriForDecoding,初始值是uri,如果有設置磁盤緩存的話,則會調(diào)用tryCacheImageOnDisk來下載并且保持圖片,此時的imageUriForDecoding則是文件File的路徑。

如果沒有設置磁盤緩存的話,則imageUriForDecoding還是uri。
關鍵則是在decodeImage,它能根據(jù)對應的uri來加載圖片。

    private Bitmap decodeImage(String imageUri) throws IOException {
        ViewScaleType viewScaleType = imageAware.getScaleType();
        ImageDecodingInfo decodingInfo = new ImageDecodingInfo(memoryCacheKey, imageUri, uri, targetSize, viewScaleType,
                getDownloader(), options);
        return decoder.decode(decodingInfo);
    }

把傳遞進來的imageUri(可能是文件的uri,也可能是圖片的uri)封裝到ImageDecodingInfo進行解析。
這里的decoder是ImageDecode,它的默認實現(xiàn)類是BaseImageDecode

@Override
    public Bitmap decode(ImageDecodingInfo decodingInfo) throws IOException {
        Bitmap decodedBitmap;
        ImageFileInfo imageInfo;

        InputStream imageStream = getImageStream(decodingInfo);
        ...
}

通過getImageStream來獲取輸入流

protected InputStream getImageStream(ImageDecodingInfo decodingInfo) throws IOException {
        return decodingInfo.getDownloader().getStream(decodingInfo.getImageUri(), decodingInfo.getExtraForDownloader());
    }

這里的Downloader默認實現(xiàn)類是BaseImageDownloader

    @Override
    public InputStream getStream(String imageUri, Object extra) throws IOException {
        switch (Scheme.ofUri(imageUri)) {
            case HTTP:
            case HTTPS:
                return getStreamFromNetwork(imageUri, extra);
            case FILE:
                return getStreamFromFile(imageUri, extra);
            case CONTENT:
                return getStreamFromContent(imageUri, extra);
            case ASSETS:
                return getStreamFromAssets(imageUri, extra);
            case DRAWABLE:
                return getStreamFromDrawable(imageUri, extra);
            case UNKNOWN:
            default:
                return getStreamFromOtherSource(imageUri, extra);
        }
    }

可以看到,在這里,已經(jīng)做了多種情況的讀取判斷。第一篇文章就有介紹到UIL可以根據(jù)不同的uri來解析圖片,其原理就是在這里。
而前面通過tryCacheImageOnDisk來下載圖片也是根據(jù)這個。這里就不一一擴展開。
這里的網(wǎng)絡下載圖片內(nèi)部則是使用HttpUrlConnection來下載的。

回到最前面LoadAndDisplayImageTask的run方法后面,當我們獲取到Bitmap后,到了

    DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom);
        runTask(displayBitmapTask, syncLoading, handler, engine);

這兩個代碼就是一個顯示任務
直接看DisplayBitmapTask類的run()方法

    @Override
    public void run() {
        if (imageAware.isCollected()) {
            L.d(LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED, memoryCacheKey);
            listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
        } else if (isViewWasReused()) {
            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被回收了或者被重用了,回調(diào)給ImageLoadingListener接口,否則就調(diào)用BitmapDisplayer去顯示Bitmap。到這里Bitmap已經(jīng)顯示加載完成,調(diào)用engine移除圖片顯示任務。

當然在最前面那里

public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
            ImageSize targetSize, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
            ...
            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);
            }
        }
    }

如果此時的顯示加載是異步的話,則交由engine的Executor線程池去處理,最終也是調(diào)用了LoadAndDisplayImageTask的run方法去加載顯示。

到這里Universal-Image-Loader的分析也算完了,從基本使用到內(nèi)存模型在加載顯示,可以看到UIL這個開源框架十分的靈活,比如建造者模式,裝飾模式,代理模式,策略模式等等,這樣方便我們?nèi)U展,實現(xiàn)我們想要的功能,當然,也帶給我們更多的想象空間。

參考資料

Android 開源框架Universal-Image-Loader完全解析(三)---源代碼解讀

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容