本文僅對UIL中一些簡單的用例作解析 java
首先,用腳本生成了該項目源碼的目錄樹,而後大體瀏覽一下文件內容,猜想其做用:git
-[ java ] -[ com ] -[ nostra13 ] -[ universalimageloader ] -[ cache ] -[ disc ] |- DiskCache.java | -[ impl ] |- BaseDiskCache.java |- LimitedAgeDiskCache.java |- UnlimitedDiskCache.java | -[ ext ] |- DiskLruCache.java |- LruDiskCache.java |- StrictLineReader.java |- Util.java | -[ naming ] |- FileNameGenerator.java |- HashCodeFileNameGenerator.java |- Md5FileNameGenerator.java | -[ memory ] |- BaseMemoryCache.java |- LimitedMemoryCache.java |- MemoryCache.java | -[ impl ] |- FIFOLimitedMemoryCache.java |- FuzzyKeyMemoryCache.java |- LargestLimitedMemoryCache.java |- LimitedAgeMemoryCache.java |- LRULimitedMemoryCache.java |- LruMemoryCache.java |- UsingFreqLimitedMemoryCache.java |- WeakMemoryCache.java | -[ core ] |- DefaultConfigurationFactory.java |- DisplayBitmapTask.java |- DisplayImageOptions.java |- ImageLoader.java |- ImageLoaderConfiguration.java |- ImageLoaderEngine.java |- ImageLoadingInfo.java |- LoadAndDisplayImageTask.java |- ProcessAndDisplayImageTask.java | -[ assist ] |- ContentLengthInputStream.java |- FailReason.java |- FlushedInputStream.java |- ImageScaleType.java |- ImageSize.java |- LoadedFrom.java |- QueueProcessingType.java |- ViewScaleType.java | -[ deque ] |- BlockingDeque.java |- Deque.java |- LIFOLinkedBlockingDeque.java |- LinkedBlockingDeque.java | -[ decode ] |- BaseImageDecoder.java |- ImageDecoder.java |- ImageDecodingInfo.java | -[ display ] |- BitmapDisplayer.java |- CircleBitmapDisplayer.java |- FadeInBitmapDisplayer.java |- RoundedBitmapDisplayer.java |- RoundedVignetteBitmapDisplayer.java |- SimpleBitmapDisplayer.java | -[ download ] |- BaseImageDownloader.java |- ImageDownloader.java | -[ imageaware ] |- ImageAware.java |- ImageViewAware.java |- NonViewAware.java |- ViewAware.java | -[ listener ] |- ImageLoadingListener.java |- ImageLoadingProgressListener.java |- PauseOnScrollListener.java |- SimpleImageLoadingListener.java | -[ process ] |- BitmapProcessor.java | -[ utils ] |- DiskCacheUtils.java |- ImageSizeUtils.java |- IoUtils.java |- L.java |- MemoryCacheUtils.java |- StorageUtils.java
官網上給出的最簡單的使用例子以下所示:github
ImageLoader imageLoader = ImageLoader.getInstance(); // Get singleton instance // Load image, decode it to Bitmap and display Bitmap in ImageView (or any other view // which implements ImageAware interface) imageLoader.displayImage(imageUri, imageView); // Load image, decode it to Bitmap and return Bitmap to callback imageLoader.loadImage(imageUri, new SimpleImageLoadingListener() { @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { // Do whatever you want with Bitmap } }); // Load image, decode it to Bitmap and return Bitmap synchronously Bitmap bmp = imageLoader.loadImageSync(imageUri);
下面一步步進行分析。緩存
看到 ImageLoader.getInstance()
這一句,應該能立刻認出這是一個singleton。代碼以下所示:網絡
public class ImageLoader { ... 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() { }
構造函數是空的。volatile
關鍵字的解釋能夠看這裏架構
接下來到imageLoader.displayImage(imageUri, imageView);
這一句。其源碼以下所示:app
public void displayImage(String uri, ImageView imageView) { displayImage(uri, new ImageViewAware(imageView), null, null, null); }
注意到ImageView
被包裝成了ImageViewAware
,ImageViewAware
繼承於ImageAware
,以下所示:異步
<ImageAware> { getWidth(); getHeight(); getScaleType(); getWrappedView(); isCollected(); getId(); setImageDrawable(); setImageBitmap(); }
一路調用到以下所示方法:ide
public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, ImageSize targetSize, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) { ... }
其源代碼以下所示,一邊猜測它的調用邏輯,一邊在關鍵點寫下注釋:函數
public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, ImageSize targetSize, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) { // 1. 作一些合法性檢查 checkConfiguration(); if (imageAware == null) { throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS); } if (listener == null) { listener = defaultListener; // 默認是SimpleImageLoadingListener,是ImageLoadingListener的空實現 } if (options == null) { options = configuration.defaultDisplayImageOptions; //在init()中初始化 } //2. 若是uri爲空,則取消對應imageAware的顯示 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; } //3. 設置圖片顯示大小 if (targetSize == null) { // 什麼是target? 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()) {// 4. 若是hit cache,則直接顯示 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 {// 5. 若是miss cache,就去下載它 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); } } }
代碼流程見文中的註釋。能夠看出三個須要重點研究的對象:Cache,Engine和Task,先說後面兩個。
首先看一下Engine的初始化流程。Enginer在ImageLoader的init中被實例化:
engine = new ImageLoaderEngine(configuration);
初始化代碼以下所示:
ImageLoaderEngine(ImageLoaderConfiguration configuration) { this.configuration = configuration; taskExecutor = configuration.taskExecutor; taskExecutorForCachedImages = configuration.taskExecutorForCachedImages; taskDistributor = DefaultConfigurationFactory.createTaskDistributor(); }
能夠看到新出現的角色:Executor。這是一個接口,代碼以下所示:
public interface Executor { void execute(java.lang.Runnable runnable); }
能夠合理猜測這是一個相似Runnable的接口,包裝了在某個線程中執行的業務。
再來看看engine的prepareDisplayTaskFor()方法:
void prepareDisplayTaskFor(ImageAware imageAware, String memoryCacheKey) { cacheKeysForImageAwares.put(imageAware.getId(), memoryCacheKey); }
還有與之對應的:
void cancelDisplayTaskFor(ImageAware imageAware) { cacheKeysForImageAwares.remove(imageAware.getId()); }
還有:
String getLoadingUriForView(ImageAware imageAware) { return cacheKeysForImageAwares.get(imageAware.getId()); }
其中 cacheKeysForImageAwares 定義以下:
private final Map<Integer, String> cacheKeysForImageAwares = Collections .synchronizedMap(new HashMap<Integer, String>());
可見prepareDisplayTaskFor保存了image可memory cache的key的映射關係。猜測是用於快速判斷當前image是否有正在被處理。
再來看看engine的sumit方法。按字面意義猜測是將一個task放到一個調度隊列中按照指定的策略執行。
/** 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(); } } private Executor createTaskExecutor() { return DefaultConfigurationFactory .createExecutor(configuration.threadPoolSize, configuration.threadPriority, configuration.tasksProcessingType /* FIFO, LIFO */); }
邏輯比較簡單,先看一下DefaultConfigurationFactory的createExecutor方法:
/** Creates default implementation of task executor */ 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對象須要指定線程池大小,線程優先級,隊列性質(FIFO/LIFO)和一個負責新建線程的工廠類。
ThreadPoolExecutor、Executor這些都是java concurrent包內置的類,詳細使用方法可見[這篇文章](),這裏不展開。
由此能夠看出,engine中維護着executor,executor負責根據預先設置好的調度策略執行task。
以ProcessAndDisplayImageTask爲例,先回憶一下調用代碼:
ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(engine, bmp, imageLoadingInfo, defineHandler(options));
engine,bmp這兩個沒什麼好說的,重點放在後二者。ImageLoadingInfo是一個Data Object,保存着此次Task的全部必要信息:
final class ImageLoadingInfo { final String uri; final String memoryCacheKey; final ImageAware imageAware; final ImageSize targetSize; final DisplayImageOptions options; final ImageLoadingListener listener; final ImageLoadingProgressListener progressListener; final ReentrantLock loadFromUriLock; ... }
defineHandler的代碼以下所示:
private static Handler defineHandler(DisplayImageOptions options) { Handler handler = options.getHandler(); if (options.isSyncLoading()) { //若是是同步加載圖片(堵塞),那麼這個handler不起做用,設爲null handler = null; } else if (handler == null && Looper.myLooper() == Looper.getMainLooper()) { handler = new Handler(); // 沒有指定Handler的狀況下,只有主線程才能執行顯示任務。 } return handler; }
這個方法的做用是返回一個執行顯示bitmap動做的handler。而後咱們回到ProcessAndDisplayImageTask:
final class ProcessAndDisplayImageTask implements Runnable { private static final String LOG_POSTPROCESS_IMAGE = "PostProcess image before displaying [%s]"; private final ImageLoaderEngine engine; private final Bitmap bitmap; private final ImageLoadingInfo imageLoadingInfo; private final Handler handler; ... }
ProcessAndDisplayImageTask繼承Runnable接口。當engine執行sumit時,ProcessAndDisplayImageTask的run方法會被調度執行。
@Override public void run() { L.d(LOG_POSTPROCESS_IMAGE, imageLoadingInfo.memoryCacheKey); BitmapProcessor processor = imageLoadingInfo.options.getPostProcessor(); Bitmap processedBitmap = processor.process(bitmap); DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(processedBitmap, imageLoadingInfo, engine, LoadedFrom.MEMORY_CACHE); // 指定了DisplayBitmapTask的來源 LoadAndDisplayImageTask.runTask(displayBitmapTask, imageLoadingInfo.options.isSyncLoading(), handler, engine); }
其中PostProcessor爲後置處理類,能夠利用這個類對已載入的bitmap進行處理(ProcessAndDisplayImageTask中bitmap已經加載完成了)。處理完後將bitmap轉交給DisplayBitmapTask類繼續進行處理。下面進入LoadAndDisplayImageTask.runTask()方法:
static void runTask(Runnable r, boolean sync, Handler handler, ImageLoaderEngine engine) { if (sync) { r.run(); } else if (handler == null) { engine.fireCallback(r); } else { handler.post(r); } }
能夠看到,若是是同步顯示的,就地執行DisplayBitmapTask的run方法。若是沒有指定handler,則在engine中執行,不然在指定的handler中執行。DisplayBitmapTask。
其源碼以下所示:
final class DisplayBitmapTask implements Runnable { ... 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() { ... } /** Checks whether memory cache key (image URI) for current ImageAware is actual */ private boolean isViewWasReused() { ... } }
重點看其中的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); } }
首先,代碼判斷bitmap是否須要顯示,若是不須要,則回調onLoadingCancelled。不然交給displayer去顯示bitmap。最後再engine中清除imageAware的記錄,並回調onLoadingComplete。
在這裏遇到兩個比較困惑的地方:isCollected和isViewWasReused。由於知足這兩個條件都不須要執行display,表明image此時是collected和view已經reused了。
當displayTask在執行時發現imageAware已經被回收了(GC或者別的緣由),就會跳過顯示這個bitmap。
isViewWasReused的代碼以下所示:
/** Checks whether memory cache key (image URI) for current ImageAware is actual */ private boolean isViewWasReused() { String currentCacheKey = engine.getLoadingUriForView(imageAware); return !memoryCacheKey.equals(currentCacheKey); }
isViewWasReused返回true則說明當前task的uri不是engine對這個imageAware最後load的uri。也就是說用戶在前一個uri尚未徹底載入的時候,又對相同imageAware發起了load task。由於以最後一次意圖加載的uri爲準,因此該次task跳過顯示bitmap。
接下來研究一下BitmapDisplayer這個類。BitmapDisplayer在DisplayImageOptions的Builder子類中賦值:
private BitmapDisplayer displayer = DefaultConfigurationFactory.createBitmapDisplayer();
最後追蹤到:
/** Creates default implementation of {@link BitmapDisplayer} - {@link SimpleBitmapDisplayer} */ public static BitmapDisplayer createBitmapDisplayer() { return new SimpleBitmapDisplayer(); } public final class SimpleBitmapDisplayer implements BitmapDisplayer { @Override public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) { imageAware.setImageBitmap(bitmap); } }
可見BitmapDisplayer只是簡單地調用了setImageBitmap方法:
public class ImageViewAware extends ViewAware { ... @Override protected void setImageBitmapInto(Bitmap bitmap, View view) { ((ImageView) view).setImageBitmap(bitmap); } }
當bitmap的memory cache存在時,運行ProcessAndDisplayImageTask,不然運行LoadAndDisplayImageTask。LoadAndDisplayImageTask涉及到網絡下載和緩存策略,重點分析其中的run方法:
@Override public void run() { if (waitIfPaused()) return; //若engine當前處於Pause狀態,則等待其Resume if (delayIfNeed()) return; //是否須要顯示一下loading image(防止閃爍) 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(); //對一個loadingInfo上鎖 Bitmap bmp; try { checkTaskNotActual(); //檢查bitmap是否被回收和當前uri是否與imageAware要顯示的uri一致 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()) { //前置處理,影響cache 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); //將bitmap放到memory cache中 } } 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);//後置處理,不影響cache if (bmp == null) { L.e(ERROR_POST_PROCESSOR_NULL, memoryCacheKey); } } checkTaskNotActual(); checkTaskInterrupted(); //檢查當前thread是否被interrupt了 } catch (TaskCancelledException e) { fireCancelEvent(); return; } finally { loadFromUriLock.unlock(); //解鎖 } //見 ProcessAndDisplayImageTask 關於這段代碼的解析 DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom); runTask(displayBitmapTask, syncLoading, handler, engine); }
代碼執行流程方面的解析見中文註釋,值得注意的有:
waitIfPaused()和delayIfNeed()方法返回的時候都調用了isTaskNotActual()方法。這個方法的做用是判斷當前的uri是否用戶最後提交的uti(isViewCollected() || isViewReused())。
tryLoadBitmap()方法
tryLoadBitmap()的代碼以下所示:
private Bitmap tryLoadBitmap() throws TaskCancelledException { Bitmap bitmap = null; try { File imageFile = configuration.diskCache.get(uri); //是否命中磁盤緩存(DiskCache類) 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())); //由Decoder來進行decode } 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()) { //嘗試從網絡下載圖片,並將其保存到disk上 imageFile = configuration.diskCache.get(uri); // 再次從disk讀入圖片 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); //在engine或指定handler上執行onLoadingFailed回調 } } } catch (IllegalStateException e) { fireFailEvent(FailType.NETWORK_DENIED, null); } catch (TaskCancelledException e) { throw e; } catch (IOException e) { L.e(e); fireFailEvent(FailType.IO_ERROR, e); } catch (OutOfMemoryError e) { L.e(e); fireFailEvent(FailType.OUT_OF_MEMORY, e); } catch (Throwable e) { L.e(e); fireFailEvent(FailType.UNKNOWN, e); } return bitmap; }
代碼執行流程方面的解析見中文註釋,留意其中的tryCacheImageOnDisk()方法:
private boolean tryCacheImageOnDisk() throws TaskCancelledException { L.d(LOG_CACHE_IMAGE_ON_DISK, memoryCacheKey); boolean loaded; try { loaded = downloadImage(); //下載image if (loaded) { int width = configuration.maxImageWidthForDiskCache; int height = configuration.maxImageHeightForDiskCache; if (width > 0 || height > 0) { L.d(LOG_RESIZE_CACHED_IMAGE_FILE, memoryCacheKey); resizeAndSaveImage(width, height); // 將image保存到disk } } } catch (IOException e) { L.e(e); loaded = false; } return loaded; } private boolean downloadImage() throws IOException { InputStream is = getDownloader().getStream(uri, options.getExtraForDownloader()); // 調用Downloader下載bitmap if (is == null) { L.e(ERROR_NO_IMAGE_STREAM, memoryCacheKey); return false; } else { try { return configuration.diskCache.save(uri, is, this); // 將bitmap直接保存到DiskCache } finally { IoUtils.closeSilently(is); } } } private boolean resizeAndSaveImage(int maxWidth, int maxHeight) throws IOException { // Decode image file, compress and re-save it boolean saved = false; File targetFile = configuration.diskCache.get(uri); if (targetFile != null && targetFile.exists()) { ImageSize targetImageSize = new ImageSize(maxWidth, maxHeight); DisplayImageOptions specialOptions = new DisplayImageOptions.Builder().cloneFrom(options) .imageScaleType(ImageScaleType.IN_SAMPLE_INT).build(); // cloneFrom方法在已有的options的基礎上添加其餘option ImageDecodingInfo decodingInfo = new ImageDecodingInfo(memoryCacheKey, Scheme.FILE.wrap(targetFile.getAbsolutePath()), uri, targetImageSize, ViewScaleType.FIT_INSIDE, getDownloader(), specialOptions); Bitmap bmp = decoder.decode(decodingInfo); // Decoder根據targetImageSize,從File中解析出Bitmap if (bmp != null && configuration.processorForDiskCache != null) { L.d(LOG_PROCESS_IMAGE_BEFORE_CACHE_ON_DISK, memoryCacheKey); bmp = configuration.processorForDiskCache.process(bmp); // 保存到disk前對bitmap進行處理 if (bmp == null) { L.e(ERROR_PROCESSOR_FOR_DISK_CACHE_NULL, memoryCacheKey); } } if (bmp != null) { saved = configuration.diskCache.save(uri, bmp); // 將處理後的Bitmap保存到DiskCache bmp.recycle(); } } return saved; }
代碼執行流程方面的解析見中文註釋。值得注意的是,爲何要屢次先寫bitmap到文件再從文件將其讀出來再使用?貼一張官網的流程圖幫助你們思考:
至此,displayImage的流程簡析完成。下面看一下第二個用例。
用例的代碼以下,做用是僅使用imageLoader的緩存和下載功能,顯示部分由用戶負責。
// Load image, decode it to Bitmap and return Bitmap to callback imageLoader.loadImage(imageUri, new SimpleImageLoadingListener() { @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { // Do whatever you want with Bitmap } });
最終會調用:
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); }
使用的依然是displayImage方法,可是傳入了一個NonViewAware類。NonViewAware不包含ImageView對象,setImageDrawable和setImageBitmap方法都是空實現。
用例代碼以下所示:
// Load image, decode it to Bitmap and return Bitmap synchronously Bitmap bmp = imageLoader.loadImageSync(imageUri);
最終調用以下所示:
public Bitmap loadImageSync(String uri, ImageSize targetImageSize, DisplayImageOptions options) { if (options == null) { options = configuration.defaultDisplayImageOptions; } options = new DisplayImageOptions.Builder().cloneFrom(options).syncLoading(true).build();// cloneFrom方法在已有的options的基礎上添加syncLoading特性 SyncImageLoadingListener listener = new SyncImageLoadingListener(); loadImage(uri, targetImageSize, options, listener); return listener.getLoadedBitmap(); }
能夠看出loadImageSync()是經過調用loadImage()來實現的。注意其傳入參數SyncImageLoadingListener對象。SyncImageLoadingListener實現了SimpleImageLoadingListener接口,在onLoadingComplete()回調中將loadedImage保存下來,其後可經過getLoadedBitmap方法取出。
首先整理出上述代碼中的關鍵類,而後大體畫出它們之間的調用關係,以下所示:
+----------------------------+ |ImageDownloader | | +------------------------+ | | | ImageLoaderEngine | | | | +------------------+ | | | | |Deque | | | | | | +----+ +----+ | | | | | | |Task| |Task| ...| | | | | | +----+ +----+ | | | | | +------------------+ | | | +------------------------+ | | +-------------+ | | |configuration| | | +-------------+ | +----------------------------+ +-------+ | Utils | +-------+ +----------------------------------------------------+ |a running task | | + | | | | | +----v----+ +---------+ | | |mem cache+-------->Processor| | | +----+--^-+ +--+------+ | | | | | | | | | | | | +----v--+--+ | +---------+ | | +-------->disk cache+-----+ +-->Displayer| | | | +----+--^--+ | +---------+ | | +---+-----+ | | | | | |Processor| | | | | | +---^-----+ +--v--+-+ | | | +----------+Decoder| | | | +-----^-+ | | | | | | | | +--v-------+ | | +-----+Downloader| | | +----------+ | | | +----------------------------------------------------+
本文只是對UIL的結構作了簡單的解析,等到用的時候踩坑了再深刻了解吧。