关于Android-Universal-Image-Loader的流程

原项目的github地址是:https://github.com/nostra13/Android-Universal-Image-Loader

一、框架的介绍 

Android-Universal-Image-Loader是一个开源的UI组件程序,该项目的目的是提供一个可重复使用的仪器为异步图像加载,缓存和显示。

有几个基本的类:

ImageLoaderConfiguration是针对图片缓存的全局配置,主要有线程类、缓存大小、磁盘大小、图片下载与解析、日志方面的配置。

ImageLoader是具体下载图片,缓存图片,显示图片的具体执行类,它有两个具体的方法displayImage(...)、loadImage(...),但是其实最终他们的实现都是displayImage(...)。

DisplayImageOptions用于指导每一个Imageloader根据网络图片的状态(空白、下载错误、正在下载)显示对应的图片,是否将缓存加载到磁盘上,下载完后对图片进行怎么样的处理。

框架的使用自行参考github上的介绍,此处不再赘述。

二、图片的加载

我们分析一下 ImageLoader.displayImage 方法

public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
 2             ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
 3         //检查UIL的配置是否被初始化
 4         checkConfiguration();
 5         if (imageAware == null) {
 6             throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS);
 7         }
 8         if (listener == null) {
 9             listener = emptyListener;
10         }
11         if (options == null) {
12             options = configuration.defaultDisplayImageOptions;
13         }
14 
15         if (TextUtils.isEmpty(uri)) {
16             engine.cancelDisplayTaskFor(imageAware);
17             listener.onLoadingStarted(uri, imageAware.getWrappedView());
18             if (options.shouldShowImageForEmptyUri()) {
19                 imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources));
20             } else {
21                 imageAware.setImageDrawable(null);
22             }
23             listener.onLoadingComplete(uri, imageAware.getWrappedView(), null);
24             return;
25         }
26         //计算Bitmap的大小,以便后面解析图片时用
27         ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());
28         String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);
29         engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);
30 
31         listener.onLoadingStarted(uri, imageAware.getWrappedView());
32         //Bitmap是否缓存在内存?
33         Bitmap bmp = configuration.memoryCache.get(memoryCacheKey);
34         if (bmp != null && !bmp.isRecycled()) {
35             L.d(LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey);
36 
37             if (options.shouldPostProcess()) {
38                 ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
39                         options, listener, progressListener, engine.getLockForUri(uri));
40                 //处理并显示图片
41                 ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(engine, bmp, imageLoadingInfo,
42                         defineHandler(options));
43                 if (options.isSyncLoading()) {
44                     displayTask.run();
45                 } else {
46                     engine.submit(displayTask);
47                 }
48             } else {
49                 //显示图片
50                 options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);
51                 listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);
52             }
53         } else {
54             if (options.shouldShowImageOnLoading()) {
55                 imageAware.setImageDrawable(options.getImageOnLoading(configuration.resources));
56             } else if (options.isResetViewBeforeLoading()) {
57                 imageAware.setImageDrawable(null);
58             }
59             
60             ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
61                     options, listener, progressListener, engine.getLockForUri(uri));
62             //启动一个线程,加载并显示图片
63             LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo,
64                     defineHandler(options));
65             if (options.isSyncLoading()) {
66                 displayTask.run();
67             } else {
68                 engine.submit(displayTask);
69             }
70         }
71     }
复制代码
我们可以看到图片的大概加载流程:

首先会根据 URI 和 ImageSize 的实例生成唯一的key。整个框架就是通过这个 key 来唯一区分图片的。如果DisplayImageOptions中配置了内存中存放缓存,就根据key判断内存中是否有该图片,如果有就处理显示图片。如果内存中没有就要寻求从硬盘上或通过网络请求获取图片。所以开启了一个 ProcessAndDisplayImageTask 线程。

private Bitmap tryLoadBitmap() throws TaskCancelledException {
 2         Bitmap bitmap = null;
 3         try {
 4             //尝试从磁盘缓存中读取Bitmap
 5             File imageFile = configuration.diskCache.get(uri);
 6             if (imageFile != null && imageFile.exists()) {
 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     }
复制代码
可以看到该线程首先会尝试从硬盘中根据 key 来获取图片,如果没有,才会发起http请求从网络上加载,inputstream 解析解码为bitmap。整个流程如下图:

三、缓存处理机制

主要谈谈内存的缓存控制,运用的是LRU(Least Recently Used)Menmory Cache。

具体实现是通过LinkedHashMap。每次获取到相关图片时,在map中寻找有没有相应的key。如果有的话该数据结构自动把该key对应的键值对移到队首,如果没有就把该key加到队首。如果缓存满了,就删除队尾元素。

@Override public V get(Object key) {
 2         /*
 3          * This method is overridden to eliminate the need for a polymorphic
 4          * invocation in superclass at the expense of code duplication.
 5          */
 6         if (key == null) {
 7             HashMapEntry<K, V> e = entryForNullKey;
 8             if (e == null)
 9                 return null;
10             if (accessOrder)
11                 makeTail((LinkedEntry<K, V>) e);
12             return e.value;
13         }
14 
15         // Replace with Collections.secondaryHash when the VM is fast enough (http://b/8290590).
16         int hash = secondaryHash(key);
17         HashMapEntry<K, V>[] tab = table;
18         for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)];
19                 e != null; e = e.next) {
20             K eKey = e.key;
21             if (eKey == key || (e.hash == hash && key.equals(eKey))) {
22                 if (accessOrder)
23                     makeTail((LinkedEntry<K, V>) e);
24                 return e.value;
25             }
26         }
27         return null;
28     }




 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值