以前的文章,在上面創建完config以後,UIl經過ImageLoader.getInstance().init(config.build());
來初始化ImageLoader對象,以後就能夠用ImageLoader來加載圖片。java
這裏,採用到單例模式來獲取ImageLoader對象,保證他全局初始化一次。再上面的分析中,咱們能夠看出單例模式的好處,建立ImageLoader對象的時候須要建立Config,而Config裏面一樣初始化了一堆對象。若是每次用到都現初始化ImageLoader,消耗太大。咱們看一下ImageLoader的init的源碼android
public synchronized void init(ImageLoaderConfiguration configuration) { if (configuration == null) { throw new IllegalArgumentException(ERROR_INIT_CONFIG_WITH_NULL); } if (this.configuration == null) { L.d(LOG_INIT_CONFIG); //建立圖片加載引擎 engine = new ImageLoaderEngine(configuration); this.configuration = configuration; } else { L.w(WARNING_RE_INIT_CONFIG); } }
若是該對象尚未config,則將傳入的config賦值給this.config。而且初始化圖片加載引擎。緩存
咱們繼續看圖片加載引擎主要執行的工做。網絡
class ImageLoaderEngine { /*ImageLoader加載配置*/ final ImageLoaderConfiguration configuration; /*任務執行者*/ private Executor taskExecutor; /*圖片緩存任務執行則*/ private Executor taskExecutorForCachedImages; /*任務分配者*/ private Executor taskDistributor; private final Map<Integer, String> cacheKeysForImageAwares = Collections .synchronizedMap(new HashMap<Integer, String>()); private final Map<String, ReentrantLock> uriLocks = new WeakHashMap<String, ReentrantLock>(); /*暫停*/ private final AtomicBoolean paused = new AtomicBoolean(false); /*網絡拒絕訪問*/ private final AtomicBoolean networkDenied = new AtomicBoolean(false); /*網絡慢*/ private final AtomicBoolean slowNetwork = new AtomicBoolean(false); private final Object pauseLock = new Object(); /** * ImageLoader引擎構造器 * @param configuration */ ImageLoaderEngine(ImageLoaderConfiguration configuration) { //初始化ImageLoader配置參數 this.configuration = configuration; //初始化三個不一樣任務的執行者 taskExecutor = configuration.taskExecutor; taskExecutorForCachedImages = configuration.taskExecutorForCachedImages; taskDistributor = DefaultConfigurationFactory.createTaskDistributor(); } /** Submits task to execution pool */ /** * 提交圖片加載和顯示任務到執行線程池中,進行運行 * @param task 具體須要執行的任務 */ void submit(final LoadAndDisplayImageTask task) { taskDistributor.execute(new Runnable() { @Override public void run() { //從文件系統緩存中獲取圖片文件 File image = configuration.diskCache.get(task.getLoadingUri()); //判斷是否已經取得了圖片 boolean isImageCachedOnDisk = image != null && image.exists(); initExecutorsIfNeed(); if (isImageCachedOnDisk) { //若是當前圖片已經緩存在本地文件系統了,直接採用taskExecutorForCachedImages來進行執行任務 taskExecutorForCachedImages.execute(task); } else { //當天圖片在本地文件系統中沒有緩存,直接採用taskExecutor來進行執行任務 taskExecutor.execute(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(); } } /** * 進行建立任務執行者 * @return */ private Executor createTaskExecutor() { return DefaultConfigurationFactory .createExecutor(configuration.threadPoolSize, configuration.threadPriority, configuration.tasksProcessingType); } /** * 獲取當前被加載ImageAware到圖片的地址 * Returns URI of image which is loading at this moment into passed {@link com.nostra13.universalimageloader.core.imageaware.ImageAware} */ String getLoadingUriForView(ImageAware imageAware) { return cacheKeysForImageAwares.get(imageAware.getId()); } /** * * Associates <b>memoryCacheKey</b> with <b>imageAware</b>. Then it helps to define image URI is loaded into View at * exact moment. */ void prepareDisplayTaskFor(ImageAware imageAware, String memoryCacheKey) { cacheKeysForImageAwares.put(imageAware.getId(), memoryCacheKey); } /** * Cancels the task of loading and displaying image for incoming <b>imageAware</b>. * * @param imageAware {@link com.nostra13.universalimageloader.core.imageaware.ImageAware} for which display task * will be cancelled */ void cancelDisplayTaskFor(ImageAware imageAware) { cacheKeysForImageAwares.remove(imageAware.getId()); } /** * Denies or allows engine to download images from the network.<br /> <br /> If downloads are denied and if image * isn't cached then {@link ImageLoadingListener#onLoadingFailed(String, View, FailReason)} callback will be fired * with {@link FailReason.FailType#NETWORK_DENIED} * * @param denyNetworkDownloads pass <b>true</b> - to deny engine to download images from the network; <b>false</b> - * to allow engine to download images from network. */ void denyNetworkDownloads(boolean denyNetworkDownloads) { networkDenied.set(denyNetworkDownloads); } /** * Sets option whether ImageLoader will use {@link FlushedInputStream} for network downloads to handle <a * href="http://code.google.com/p/android/issues/detail?id=6066">this known problem</a> or not. * * @param handleSlowNetwork pass <b>true</b> - to use {@link FlushedInputStream} for network downloads; <b>false</b> * - otherwise. */ void handleSlowNetwork(boolean handleSlowNetwork) { slowNetwork.set(handleSlowNetwork); } /** * Pauses engine. All new "load&display" tasks won't be executed until ImageLoader is {@link #resume() resumed}.<br * /> Already running tasks are not paused. * 暫停任務運行 */ void pause() { paused.set(true); } /** * Resumes engine work. Paused "load&display" tasks will continue its work. * 任務恢復運行 */ void resume() { paused.set(false); synchronized (pauseLock) { pauseLock.notifyAll(); } } /** * 中止ImageLoader引擎,取消全部正在運行或者掛起的圖片顯示任務,而且清除內部的數據 * Stops engine, cancels all running and scheduled display image tasks. Clears internal data. * <br /> * <b>NOTE:</b> This method doesn't shutdown * {@linkplain com.nostra13.universalimageloader.core.ImageLoaderConfiguration.Builder#taskExecutor(java.util.concurrent.Executor) * custom task executors} if you set them. */ void stop() { if (!configuration.customExecutor) { ((ExecutorService) taskExecutor).shutdownNow(); } if (!configuration.customExecutorForCachedImages) { ((ExecutorService) taskExecutorForCachedImages).shutdownNow(); } cacheKeysForImageAwares.clear(); uriLocks.clear(); } void fireCallback(Runnable r) { taskDistributor.execute(r); } ReentrantLock getLockForUri(String uri) { ReentrantLock lock = uriLocks.get(uri); if (lock == null) { lock = new ReentrantLock(); uriLocks.put(uri, lock); } return lock; } AtomicBoolean getPause() { return paused; } Object getPauseLock() { return pauseLock; } boolean isNetworkDenied() { return networkDenied.get(); } boolean isSlowNetwork() { return slowNetwork.get(); } }
上面的代碼中,核心的部分就是建立了任務執行器和圖片緩存執行器,而且在submit方法中針對提交的任務,選擇不一樣的執行器執行。app
ImageLoader關於加載顯示圖片,有以下幾種用法,咱們依次分析一下。框架
displayImage(), loadImage()
先看loadImage()ide
下面的loadImage全部的重載方法。post
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) { }
不論是幾個參數的loadImage,最後都會重載下面的方法。ui
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); }
若是沒有指定targetImageSize以及options就會採用config默認的提供,而後根據targetImageSize以及uri生成一個NonViewAware,最終經過displayImage來加載。this
targetImageSize是一個ImageSize對象,該類是image尺寸的封裝類,config的默認值是屏幕的寬高。
options是採用config默認建立,config的默認值是faultDisplayImageOptions = DisplayImageOptions.createSimple();
這個初始化都是建立的DisplayImageOptions都是默認值,基本什麼屬性都是false或者null或者0
接下來,咱們看一下NonViewAware類,NonViewAware是實現了ImageAware接口的一個類,ImageAware接口主要定義了圖片處理和顯示所須要的方法和屬性。因此NonViewAware也只是對傳入的參數進行封裝,來提供一個外部訪問的接口。
真正顯示圖片的方法都是displayImage,displayImage方法和loadImage同樣,提供了多種參數,displayImage的重載方法要多一些,由於displayImage方法有一類是接受ImageView而另外一類是接受ImageAware。
下面是不管如何都是最終調用的方法:
代碼以下:
public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, ImageSize targetSize, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) { //進行檢查ImageLoader全局相關配置 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()); //進行判斷是否給imageview添加一個空地址的資源圖片 if (options.shouldShowImageForEmptyUri()) { imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources)); } else { imageAware.setImageDrawable(null); } //直接加載回調加載成功 listener.onLoadingComplete(uri, imageAware.getWrappedView(), null); return; } //=============圖片地址存在===================== if (targetSize == null) { //若是圖片顯示的目標大小沒有設置的,那麼就使用默認大小尺寸便可 targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize()); } //根據地址和圖片目標尺寸信息,生成緩存key String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize); engine.prepareDisplayTaskFor(imageAware, memoryCacheKey); //開始進行加載圖片 listener.onLoadingStarted(uri, imageAware.getWrappedView()); //首先根據key去緩存中獲取是否還存在該圖片 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); } } 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); } } }
具體的執行過程已經經過代碼註釋了,下面梳理下流程。
首先,針對爲null的屬性,初始化這些屬性,而後判斷傳入的uri是否是爲空,若是爲空,則根據options.shouldShowImageForEmptyUri來判斷是否顯示先設置好的圖片。
若是傳入的uri是存在的,則根據地址和圖片目標尺寸的信息來生成緩存key,而後執行
prepareDisplayTaskFor方法。
再根據key來判斷緩存中是否存在該圖片,若是存在,就判斷options的shouldPostProcess,若是爲true就建立一個ProcessAndDisplayImageTask對象,經過圖片加載引擎來加載。若是返回的值false,則調用options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);
若是緩存不存在,則直接經過網絡加載該圖片,加載的方式是構建LoadAndDisplayImageTask對象,經過圖片加載引擎來判斷。
在這裏,關於圖片的緩存相關的內容,先不分析。接下來主要分析的是圖片如何執行這兩種不一樣的任務的。
第一種,在有緩存的狀況下,經過判斷shouldPostProcess爲true來讓圖片引擎處理任務,這裏的shouldPostProcess是指在拿到bitmap以後是否進行後續的操做,判斷標準就是postProcess是否爲null.
若是不爲null,也就是shouldPostProcess爲true,則執行下面的代碼:
下面,咱們來看一下,圖片引擎是如何執行ProcessAndDisplayImageTask。
void submit(ProcessAndDisplayImageTask task) { initExecutorsIfNeed(); taskExecutorForCachedImages.execute(task); }
內部直接調用taskExecutorForCachedImages去執行task,因此主要看ProcessAndDisplayImageTask的task構造。
final class ProcessAndDisplayImageTask implements Runnable { private static final String LOG_POSTPROCESS_IMAGE = "PostProcess image before displaying [%s]"; /*ImageLoader引擎*/ private final ImageLoaderEngine engine; private final Bitmap bitmap; /*ImageLoader信息封裝對象*/ private final ImageLoadingInfo imageLoadingInfo; private final Handler handler; /** * 圖片處理顯示任務構造器 * @param engine * @param bitmap * @param imageLoadingInfo * @param handler */ public ProcessAndDisplayImageTask(ImageLoaderEngine engine, Bitmap bitmap, ImageLoadingInfo imageLoadingInfo, Handler handler) { this.engine = engine; this.bitmap = bitmap; this.imageLoadingInfo = imageLoadingInfo; this.handler = handler; } @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); //執行任務 LoadAndDisplayImageTask.runTask(displayBitmapTask, imageLoadingInfo.options.isSyncLoading(), handler, engine); } }
這邊能夠看出,task內部run方法裏面,首先獲得一個BitmapProcessor,而後經過該processor去處理bitmap,而後將處理後的bitmap以及其餘信息封裝成了DisplayBitmapTask,而後最終仍是執行了LoadAndDisplayImageTask的runTask方法
下面將看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分發到主線程執行 handler.post(r); } }
這邊,直接在UI線程displayBitmapTask,後面再看displayBitmapTask的內部實現。
回到shouldPostProcess的判斷那裏,若是爲false,則直接調用BitmapDisplay顯示圖片,這裏傳入的是SimpleBitmapDisplayer
再回到緩存判斷那裏,上面的代碼都是在有內存緩存的狀況下,執行的。看一下在無內存緩存時,執行的細節。
//緩存中不存在該圖片 經過網絡加載 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); }
經過源碼能夠看出,首先判斷要不要進行顯示加載中的View,而後構建圖片加載信息,經過圖片加載信息構建LoadAndDisplayImageTask對象,執行去run方法。
上面,咱們已經分析了其runTask方法,該方法比較簡單,此次咱們看一下run方法。
public void run() { //若是當前狀態是暫停 當前任務直接返回 if (waitIfPaused()) return; //若是當前狀態須要等待 當前任務直接返回 if (delayIfNeed()) return; 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 { //從緩存中獲取到圖片信息 //設置圖片來源信息 --Memory Cache 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); }
其流程圖以下:
能夠看到這邊,利用的圖片三級緩存,第一級是內存緩存,若是內存緩存沒有則利用二級緩存,從文件中去讀取,若是文件中有,則從文件中取出bitmap,若是沒有則從網絡下載。
這邊從文件中bitmap的方法是tryLoadBitmap,下面主要看一下這個方法
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對象 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); } } } 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; }
首先是從diskcache中,取出File,而後對file進行轉換。若是轉換後的bitmap爲null,則從網絡獲取圖片,獲取圖片的方法調用是在options.isCacheOnDisk() && tryCacheImageOnDisk()
該判斷首先判斷是否存儲在文件中,若是不存在文件中,就不執行後面的網絡獲取。
private boolean tryCacheImageOnDisk() throws TaskCancelledException { L.d(LOG_CACHE_IMAGE_ON_DISK, memoryCacheKey); boolean loaded; try { //圖片下載而且保存本地 loaded = downloadImage(); 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); // TODO : process boolean result } } } catch (IOException e) { L.e(e); loaded = false; } return loaded; }
downloadImage是執行網絡請求的方法,其內部經過BaseImageDownloader
進行下載,其內部的網絡庫是HttpURLConnection
,下載後的圖片,根據設定的文件存儲的最大寬高,進行縮放與保存。
這樣,就在文件緩存中緩存了圖片,在回到上面LoadAndDisplayTask的run方法,在獲得bitmap以後,就會判斷是否要 對bitmap進行預處理,預處理完的bitmap所有會緩存到內存緩存中。上面的操做都是創建在bitmap中內存緩存中取沒有取出來的狀況,若是取出來就直接獲得bitmap,而後從bitmap判斷是否進行後續的處理。
這裏,簡單說一下 preProcessor以及postProcessor,preProcessor是指對圖片進行預處理,好比加水印,若是加水印的圖片都會緩存到內存,postProcessor是對取出的bitmap作一些後續的操做,操做後將顯示出來。
最後獲得的Bitmap會封裝成DisplayBitmapTask,調用上面提到的runtask方法,進行處理。
這樣,到此,發起圖片獲取需求,到圖片通過內存緩存,文件緩存,網絡獲取後獲得,而後再經過Handler回到UI線程的流程就分析完畢了。
其實,整個流程很是的簡單,清晰。只不過在考慮到了多種狀況,使得代碼看上去不少。
下面,將分析圖片加載框架最重要的一部分,緩存的設計。