本文仅对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的结构作了简单的解析,等到用的时候踩坑了再深刻了解吧。