之前花了一些时间,好好看了下这个框架,于是决定再重新梳理一下,把整个处理方法和流程过一遍,俗话说:温故而知新嘛
关于Universal-Image-Loader此框架的各种优点,稍微介绍下,网上应该也有不少了:
1. 提供了内存缓存和磁盘缓存两种缓存方式,同时还可以通过实现它提供的接口来实现自己的内存缓存和磁盘缓存,并且可设置对应的缓存空间大小及缓存图片的最大尺寸
2. 提供了线程池的多线程下载,支持自定义的线程池,可设置线程个数和优先级
3. 可以通过实现它提供的接口来实现自定义图片下载方式,这样你就可以方便的使用其他的图片下载的开源框架:比如OKHTTP等,同时还支持监听图片下载
4.可通过实现接口自定义内存缓存和磁盘缓存,你也可以DIY属于自己的缓存方法咯
5.可自定义磁盘缓存最大文件个数,以及文件名生成方式,以免图片数量太多占用空间
6.可通过实现接口自定义图片解析和图片处理(下载完图片后):ImageDecoder BitmapProcessor,并且能控制图片的加载过程,比如:开始加载,暂停,重新加载,这样可以来优化你的app,使得app的使用更加舒适,人性化;
8.支持对图片的缩放,旋转等,根据控件大小来对图片进行裁剪
9.可选择任务处理的方式:FIFO或者LIFO
10.可选择是否允许在内存保存同一个图片多个尺寸的缓存
11.支持网速较差的图片下载
以上只是自己的总结,可能有所遗漏。
一. 下面我主要介绍此框架的内部流程,关于它的具体使用,可以看看其附带的demo
此框架的使用流程大致就是上图:
1. 生成配置参数;2. 使用配置初始化ImageLoader; 3. 定义相关参数,可实现图片下载监听等,调用displayImage或者loadImage开始下载显示图片;
二. 下面主要介绍displayImage的流程:
ImageLoaderEngine:使用线程池中的线程来处理Task
1 //图片没有被内存缓存
2 void submit(final LoadAndDisplayImageTask task) {
3 taskDistributor.execute(new Runnable() {
4 @Override
5 public void run() {
6 //检查图片是否已经存在磁盘
7 File image = configuration.diskCache.get(task.getLoadingUri());
8 boolean isImageCachedOnDisk = image != null && image.exists();
9 initExecutorsIfNeed();
10 //图片在磁盘缓存
11 if (isImageCachedOnDisk) {
12 taskExecutorForCachedImages.execute(task);
13 } else {
14 taskExecutor.execute(task);
15 }
16 }
17 });
18 }
19
20 /** Submits task to execution pool */
21 //图片已被内存缓存,
22 void submit(ProcessAndDisplayImageTask task) {
23 initExecutorsIfNeed();
24 taskExecutorForCachedImages.execute(task);
25 }
View Code
三. 从图上可以看出,image的下载,显示其核心是LoadAndDisplayImageTask和ProcessAndDisplayImageTask,这两个对象都是实现了Runnable接口,下面我们就分析下这两个对象
1. ProcessAndDisplayImageTask: 比较简单,最后处理还是通过LoadAndDisplayImageTask来实现
1 @Override
2 public void run() {
3 L.d(LOG_POSTPROCESS_IMAGE, imageLoadingInfo.memoryCacheKey);
4
5 BitmapProcessor processor = imageLoadingInfo.options.getPostProcessor();
6 Bitmap processedBitmap = processor.process(bitmap);
7 DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(processedBitmap, imageLoadingInfo, engine,
8 LoadedFrom.MEMORY_CACHE);
9 LoadAndDisplayImageTask.runTask(displayBitmapTask, imageLoadingInfo.options.isSyncLoading(), handler, engine);
10 }
View Code
2. LoadAndDisplayImageTask:逻辑也比较清楚,当前图片已经在缓存了就直接进行显示,否则就先下载然后显示
1 @Override
2 public void run() {
3 if (waitIfPaused()) return;
4 if (delayIfNeed()) return;
5
6 //用于判断当前图片url是否正在被使用
7 ReentrantLock loadFromUriLock = imageLoadingInfo.loadFromUriLock;
8 L.d(LOG_START_DISPLAY_IMAGE_TASK, memoryCacheKey);
9 if (loadFromUriLock.isLocked()) {
10 L.d(LOG_WAITING_FOR_IMAGE_LOADED, memoryCacheKey);
11 }
12
13 //memoryCacheKey加锁
14 loadFromUriLock.lock();
15 Bitmap bmp;
16 try {
17 //if task is not actual (target ImageAware is collected by GC or the image URI of
18 //this task doesn't match to image URI which is actual for current ImageAware at this moment
19 //目标控件是否被回收,图片的下载地址已经和目标控件不匹配:键值对
20 checkTaskNotActual();
21
22 bmp = configuration.memoryCache.get(memoryCacheKey);
23 //内存中没有此图片
24 if (bmp == null || bmp.isRecycled()) {
25 //下载图片
26 bmp = tryLoadBitmap();
27 if (bmp == null) return; // listener callback already was fired
28
29 //检查当前目标控件(imageAware)是否是合法,如果被回收或者已经被其他图片使用,则抛出异常
30 checkTaskNotActual();
31 //检查任务是否被中断,如果被中断,抛出异常
32 checkTaskInterrupted();
33
34 //是否对图片进行预处理(用户实现)
35 if (options.shouldPreProcess()) {
36 L.d(LOG_PREPROCESS_IMAGE, memoryCacheKey);
37 bmp = options.getPreProcessor().process(bmp);
38 if (bmp == null) {
39 L.e(ERROR_PRE_PROCESSOR_NULL, memoryCacheKey);
40 }
41 }
42
43 //是否将图片保存在内存中
44 if (bmp != null && options.isCacheInMemory()) {
45 L.d(LOG_CACHE_IMAGE_IN_MEMORY, memoryCacheKey);
46 configuration.memoryCache.put(memoryCacheKey, bmp);
47 }
48 } else {
49 loadedFrom = LoadedFrom.MEMORY_CACHE;
50 L.d(LOG_GET_IMAGE_FROM_MEMORY_CACHE_AFTER_WAITING, memoryCacheKey);
51 }
52
53 //图片后置处理(用户实现)
54 if (bmp != null && options.shouldPostProcess()) {
55 L.d(LOG_POSTPROCESS_IMAGE, memoryCacheKey);
56 bmp = options.getPostProcessor().process(bmp);
57 if (bmp == null) {
58 L.e(ERROR_POST_PROCESSOR_NULL, memoryCacheKey);
59 }
60 }
61 checkTaskNotActual();
62 checkTaskInterrupted();
63 } catch (TaskCancelledException e) {
64 //取消任务
65 fireCancelEvent();
66 return;
67 } finally {
68 loadFromUriLock.unlock();
69 }
70
71 //显示图片
72 DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom);
73 runTask(displayBitmapTask, syncLoading, handler, engine);
74 }
View Code
不在缓存时,需要下载图片
1 private Bitmap tryLoadBitmap() throws TaskCancelledException {
2 Bitmap bitmap = null;
3 try {
4 //是否存在磁盘
5 File imageFile = configuration.diskCache.get(uri);
6 if (imageFile != null && imageFile.exists() && imageFile.length() > 0) {
7 L.d(LOG_LOAD_IMAGE_FROM_DISK_CACHE, memoryCacheKey);
8 loadedFrom = LoadedFrom.DISC_CACHE;
9
10 checkTaskNotActual();
11 bitmap = decodeImage(Scheme.FILE.wrap(imageFile.getAbsolutePath()));
12 }
13 //不存在磁盘,或者从磁盘解析图片失败
14 if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
15 L.d(LOG_LOAD_IMAGE_FROM_NETWORK, memoryCacheKey);
16 loadedFrom = LoadedFrom.NETWORK;
17
18 String imageUriForDecoding = uri;
19 if (options.isCacheOnDisk() && tryCacheImageOnDisk()) {
20 imageFile = configuration.diskCache.get(uri);
21 if (imageFile != null) {
22 imageUriForDecoding = Scheme.FILE.wrap(imageFile.getAbsolutePath());
23 }
24 }
25
26 checkTaskNotActual();
27 bitmap = decodeImage(imageUriForDecoding);
28
29 if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
30 fireFailEvent(FailType.DECODING_ERROR, null);
31 }
32 }
33 } catch (IllegalStateException e) {
34 fireFailEvent(FailType.NETWORK_DENIED, null);
35 } catch (TaskCancelledException e) {
36 throw e;
37 } catch (IOException e) {
38 L.e(e);
39 fireFailEvent(FailType.IO_ERROR, e);
40 } catch (OutOfMemoryError e) {
41 L.e(e);
42 fireFailEvent(FailType.OUT_OF_MEMORY, e);
43 } catch (Throwable e) {
44 L.e(e);
45 fireFailEvent(FailType.UNKNOWN, e);
46 }
47 return bitmap;
48 }
View Code
最后, 在线程中执行显示
1 static void runTask(Runnable r, boolean sync, Handler handler, ImageLoaderEngine engine) {
2 //图片同步显示,立即执行
3 //否则,handler为空,使用Executor taskDistributor线程池执行
4 //否则,加入循环队列,等待执行
5 if (sync) {
6 r.run();
7 } else if (handler == null) {
8 engine.fireCallback(r);
9 } else {
10 handler.post(r);
11 }
12 }
View Code
此框架的整个大体流程就是这样,后面会对某些方面继续更深入的解析,比如内存缓存,磁盘缓存,此架构的设计等