以前的文章,在上面创建完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线程的流程就分析完毕了。
其实,整个流程很是的简单,清晰。只不过在考虑到了多种状况,使得代码看上去不少。
下面,将分析图片加载框架最重要的一部分,缓存的设计。