天天看點

圖檔加載架構--ImageLoader實作(二)

前言

上篇簡單介紹了UniversalImageLoader的使用,分析了下源代碼,大緻可以進行如下劃分,本文将分析圖檔加載的Imageloader實作,。

圖檔加載架構--ImageLoader實作(二)

正文

通過ImageLoader執行個體對象,調用public void displayImage(String uri, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener)發放将開始加載圖檔,具體過程可以分為幾個階段:

 合法性檢查

主要是初始化檢查和參數檢查,可能會抛出異常或是下載下傳不受幹擾可以繼續;有傳入的圖檔位址為空,imageview為空,圖檔配置執行個體為空,過程監聽接口為空四種情況。

if (configuration == null) { 
    throw new RuntimeException(ERROR_NOT_INIT); 
} 
if (imageView == null) { 
    Log.w(TAG, ERROR_WRONG_ARGUMENTS); 
    return; 
} 
if (listener == null) { 
    listener = emptyListener; 
} 
if (options == null) { 
    options = configuration.defaultDisplayImageOptions; 
}

if (uri == null || uri.length() == 0) { 
    cacheKeyForImageView.remove(imageView); 
    listener.onLoadingStarted(); 
    if (options.isShowImageForEmptyUri()) { 
        imageView.setImageResource(options.getImageForEmptyUri()); 
    } else { 
        imageView.setImageBitmap(null); 
    } 
    listener.onLoadingComplete(null); 
    return; 
}       

如果沒有初始化ImageLoader是比較嚴重的,将直接抛出運作時異常,控件ImageView為空時不影響,可以直接退出,不再下載下傳,圖檔配置和監聽接口為空則将啟用預設值,至于圖檔url的話,由于初始化時已經執行個體化了預設值的情形,是以将顯示本地設定的預設圖檔,同時将控件移出HashMap。

加載準備

這裡的準備操作一個是擷取圖檔的尺寸參數,然後根據這個參數和url生成标記ImageView的key,最後以key-value的形式存入HashMap中備用,

targetSize = getImageSizeScaleTo(imageView); 
String memoryCacheKey = MemoryCacheKeyUtil.generateKey(uri, targetSize); 
cacheKeyForImageView.put(imageView, memoryCacheKey);       

加載操作

加載分為調用記憶體緩存和本地緩存/網絡下載下傳,根據上一步加載準備中得到的key擷取bitmap,這個過程比較發雜,單獨介紹。

if (bmp != null && !bmp.isRecycled()) { 
if (configuration.loggingEnabled) Log.i(TAG, String.format(LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey)); 
listener.onLoadingStarted(); 
Bitmap displayedBitmap = options.getDisplayer().display(bmp, imageView); 
imageView.setImageBitmap(displayedBitmap); 
listener.onLoadingComplete(bmp); 
}      

根據得到的bitmap,如果不為空且未被标記為回收狀态,那麼就可以使用這個緩存的bitmap,調用監聽接口的onLoadingStarted()處理一些加載錢的操作,然後對bitmap做一些顯示前的操作,這個就用到傳入進來的圖檔顯示的option配置,如果沒傳這個值,那啟用預設值,應該是不對bitmap操作,這個預設的option使用SimpleBitmapDisplayer 執行個體,檢視其源代碼,果然,是直接将bitmap設定給ImageView然後對外傳回原來的Bitmap,對于這種情況後面的在此設定bitmap給imageview其實有些累贅,重複操作了。

public final class SimpleBitmapDisplayer implements BitmapDisplayer { 
    @Override 
    public Bitmap display(Bitmap bitmap, ImageView imageView) { 
        imageView.setImageBitmap(bitmap); 
        return bitmap; 
    } 
}      

接下來調用監聽接口的onLoadingComlete();到顯示記憶體緩存圖檔的操作就結束了,下面介紹第二種情況,就是從磁盤緩存/網新下載下傳圖檔。

先調用監聽接口的onLoadingStarted();接着顯示一個下載下傳過程中的圖檔或則幹脆在下載下傳時不顯示任何圖檔。接着檢查一下線程池是否初始化并正常工作中checkExecutors(),檢視源代碼可以發現,項目用與下載下傳的的task其實是通過ExecutorService來管理,如果還不知道什麼是ExecutorService趕緊去補一下java的多線程并發程式設計吧。

圖檔加載架構--ImageLoader實作(二)
private void checkExecutors() { 
    if (imageLoadingExecutor == null || imageLoadingExecutor.isShutdown()) { 
        imageLoadingExecutor = Executors.newFixedThreadPool(configuration.threadPoolSize, configuration.displayImageThreadFactory); 
    } 
    if (cachedImageLoadingExecutor == null || cachedImageLoadingExecutor.isShutdown()) { 
        cachedImageLoadingExecutor = Executors.newFixedThreadPool(configuration.threadPoolSize, configuration.displayImageThreadFactory); 
    } 
}       

為了下載下傳圖檔這裡把必要的圖檔資訊做了一個封裝傳給工作線程。ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageView, targetSize, options, listener, getLockForUri(uri));

現在一起看一下他的工作線程是怎麼寫的。LoadAndDisplayImageTask displayImageTask = new LoadAndDisplayImageTask(configuration, imageLoadingInfo, new Handler());

這裡的最後一個參數是我們熟悉的Handler 執行個體,可以預測這個task類應該是Runnale的是子類,在run()中根據情況向handler發送處理操作請求。(未完待續)