android中最让人头疼的莫过于从网络上获取图片、显示、回收,任何一个环节有问题都可能直接OOM。尤其是在列表页,会加载大量网络上的图片,每当快速滑动列表的时候,就会很卡,甚至会因为内存溢出而崩溃。
这个时候就轮到imageloader上场表演了。Imageloader的目的是为了实现异步的网络图片加载、缓存及显示,支持多线程异步加载。
Imageloader的工作原理是这样的:在显示图片的时候,它会先在内存中查找;如果没有就去本地查找;如果还没有,就开一个新的线程去下载这张图片,下载成功后会把图片缓存到内存和本地。
上面是 UIL 的总体设计图。整个库分为ImageLoaderEngine,Cache及ImageDownloader,ImageDecoder,BitmapDisplayer,BitmapProcessor五大模块,其中Cache分为MemoryCache和DiskCache两部分。
简单的讲就是ImageLoader收到加载及显示图片的任务,并将它交给ImageLoaderEngine,ImageLoaderEngine分发任务到具体线程池去执行,任务通过Cache及ImageDownloader获取图片,中间可能经过BitmapProcessor和ImageDecoder处理,最终转换为Bitmap交给BitmapDisplayer在ImageAware中显示。
app初始启动的时候需要调用ImageLoader的init(ImageLoaderConfiguration configuration)方法(常在application的onCreate方法中执行这个方法),这个方法主要作用是初始化一个ImageLoaderEngine和把ImageLoaderConfiguration注入ImageLoader中。
下面,我们在去图片的时候只需要调用ImageLoader的displayImage方法即可,我们看一下这个方法的具体实现:这个方法比较长,这里大致分析一下:具体逻辑都在下面这幅图中:
使用的是一个简单的建造者模式.
重点分析它的run方法,具体看一张流程图
Bitmap 内存缓存接口,需要实现的接口包括 get(…)、put(…)、remove(…)、clear()、keys()。
BaseMemoryCache
实现了MemoryCache主要函数的抽象类,以 Map> softMap 做为缓存池,利于虚拟机在内存不足时回收缓存对象。提供抽象函数:
protected abstract Reference createReference(Bitmap value)
表示根据 Bitmap 创建一个 Reference 做为缓存对象。Reference 可以是 WeakReference、SoftReference 等。
图片的磁盘缓存接口。
主要函数:
(1) File get(String imageUri)
根据原始图片的 uri 去获取缓存图片的文件。
(2) boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener)
保存 imageStream 到磁盘中,listener 表示保存进度且可在其中取消某些段的保存。
(3) boolean save(String imageUri, Bitmap bitmap)
保存图片到磁盘。
(4) boolean remove(String imageUri)
根据图片 uri 删除缓存图片。
(5) void close()
关闭磁盘缓存,并释放资源。
(6) void clear()
清空磁盘缓存。
(7) File getDirectory()
得到磁盘缓存的根目录。
BaseDiskCache
一个无大小限制的本地图片缓存,实现了DiskCache主要函数的抽象类。
图片缓存在cacheDir文件夹内,当cacheDir不可用时,则使用备库reserveCacheDir。
主要函数:
(1). save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener)
先根据imageUri得到目标文件,将imageStream先写入与目标文件同一文件夹的 .tmp 结尾的临时文件内,若未被listener取消且写入成功则将临时文件重命名为目标文件并返回 true,否则删除临时文件并返回 false。
(2). save(String imageUri, Bitmap bitmap)
先根据imageUri得到目标文件,通过Bitmap.compress(…)函数将bitmap先写入与目标文件同一文件夹的 .tmp 结尾的临时文件内,若写入成功则将临时文件重命名为目标文件并返回 true,否则删除临时文件并返回 false。
(3). File getFile(String imageUri)
根据 imageUri 和 fileNameGenerator得到文件名,返回cacheDir内该文件,若cacheDir不可用,则使用备库reserveCacheDir。
在ImageAware中显示 bitmap 对象的接口。可在实现中对 bitmap 做一些额外处理,比如加圆角、动画效果。
图片下载接口
BaseImageDownloader
public InputStream getStream(String imageUri, Object extra) throws IOException {
switch(BaseImageDownloader.SyntheticClass_1.$SwitchMap$com$nostra13$universalimageloader$core$download$ImageDownloader$Scheme[Scheme.ofUri(imageUri).ordinal()]) {
case 1:
case 2:
return this.getStreamFromNetwork(imageUri, extra);
case 3:
return this.getStreamFromFile(imageUri, extra);
case 4:
return this.getStreamFromContent(imageUri, extra);
case 5:
return this.getStreamFromAssets(imageUri, extra);
case 6:
return this.getStreamFromDrawable(imageUri, extra);
case 7:
default:
return this.getStreamFromOtherSource(imageUri, extra);
}
}
从这段代码可以看出,它支持多种方式去获取流.不仅仅是网络请求。
感觉这个代码挺庞大的,阅读暂时先告一段落,等待后续有新的缘分和领悟再来阅读,再来更新这篇博客。
http://a.codekk.com/detail/Android/huxian99/Android%20Universal%20Image%20Loader%20%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90