博客地址:http://my.oschina.net/lifj/blog/313866
-------- 2014年8月19日 BY 拉风的道长
开源项目地址
https://developer.android.com/training/displaying-bitmaps/index.html
项目简介
如果图片资源是静态的,当我们要在View上显示图片时,只需要简单的将图片赋值给ImageView就可以了,但如果需要浏览网络上的图片时该如何做呢?有可能图片很大,有可能网速很慢并且不稳定,这种情况下该如何增加用户体验。Android官方的BitmapFun示例程序已经给了我们很好的解决方法 - 其实万变不离其中,还是采用了提升性能的两种常用方法:异步和缓存。
项目分析
下载源码之后(最新的代码可能和本文分析的代码不同,但是思路应该没有变),用Eclipse 打开,开始分析吧。从哪里入手分析呢?从使用方法入手!按照使用方法,从头到尾顺藤摸瓜走一遍。这样,基本上就能把整个代码逻辑走遍了。
ImageWorker $ loadImage
先从ImageCache中获取图片。ImageCache是什么?是我们缓存的主要类:包括内存和磁盘。
这里主要从mMemoryCache获取图片,然后返回。
上面从ImageCache中获取缓存之后,如果不为空,就直接设置图片。如果为空,就进入else
先调用了cancelPotentailWork方法:
先获取BitmapWorkerTask,看看是不是需要取消。
这里,获取BitmapWorkerTask时,从AnsyncDrawable中获取这个task。
获取到这个taskt,然后通过BitmapWorkerTask的data来判断:如果为空或者新旧不相等,就取消。然后返回ture。
BitmapWorkerTask的data是什么?
BitmapWorkerTask是AnsncTask。data是执行BitmapWorkerTask时传过来的第一个参数。
在哪里创建的?其实就是在else中创建的:
如果是需要取消之前的任务,则进入到else里面。这里,就是启动一个新的下载(读取磁盘或者网络)过程。
首先创建一个Task,
然后创建一个AsyncDrawable。在创建这个drawable的时候,就把task传了过去。这样,在上面判断是不是需要取消的时候,就能获取到对应的task了。
接着把这个asyncDrawable设置到imgeView中。
最后执行这个BitmapWorkerTask。
可以看出:主要的方法就是BitmapWorkerTask。
总体流程是:先从diskCache中读取,如果没有,就再调用processBitmap。最终,都会把这个图片添加到imageCache中。这也就是一开始loadImage时,从imageCache中获取的来源。
从ImageCache中读取的条件是:缓存不为空,任务没有被取消,获取和任务绑定的ImageView不为空,以及没有被提前取消。
获取绑定的imageview 是防止Imageview被重新设置了task,然后导致两次绑定的task不同。
主要处理图片的过程:
processBitmap是抽象方法,具体的实现类是在ImageResizer中。
在IamgeFetcher中也有实现:
这里先放一下吧。回到之前的。
Task结束之后,执行onPostExcute
这里又一次检查imageview的匹配。
其实有时候,你会惊讶于谷歌的人思维真紧密,这边都想到要判断一下。
就我个人的经验,其实,有时候,不是想起来的,不是思维缜密。而是出现了bug,然后加上的这些判断。所以,有时候读源码,看到别人的判断感觉很没有必要或者说很细,其实都是bug的补丁。
Task就是这样,AsyncDrawable也是很重要的一个类:
主要就是保存了一个task。
其实这里保证了图片和ImageView的一致性,防止图片错乱:
Task绑定住ImageView,ImageView绑定住AsyncDrawable,AsyncDrawable绑定住Task。
当设置图片的时候(在Task中设置图片):从绑定的ImageView中获取AsyncDrawable,再获取绑定的Task。然后比较这两个task是不是相同。为什么可以这么做呢?当一个ImageView要重新加载图片的时候,会给ImageView重新设置AsyncDrawable。所以,即使重用了,如果ImageView对应的Task不同,那么就说明图片已经修改了,这次的Task就不应该设置图片了。
接下来就是缓存的实现:
缓存分为磁盘缓存和内存缓存。
LRU是Least Recently Used 近期最少使用算法。也就是:当内存(或者磁盘)容量不够时,把最近使用最少的内容去除掉,从而腾出空间增加新内容。
初始化的过程:
先创建diskCache,然后创建memoryCache。主要的参数涉及到一个memSize,定义了cache的大小。内存cache的创建比较简单,主要是diskCache:
上面的代码是创建一个新的diskCache。通过一个dir的地址,创建一个文件夹,同时判断可用空间是不是超过最大的memSize。这决定了diskCache是否创建成功。
diskCache和memoryCache的放置数据,都是key-value型的,这个没什么好讲的。