前言
上篇簡單介紹了UniversalImageLoader的使用,分析了下源代碼,大致可以進行如下划分,本文將分析圖片加載的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的多線程並發編程吧。
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發送處理操作請求。(未完待續)