前言
上一次分析了关于Volley
最核心的原理,对Volley
如何处理网络请求做了一个全面的剖析。还没有了解的可以先了解一下Android Volley源码分析(一),这次我再来对它的图片加载原理进行自我讲解,希望能帮助到一部分人,也能巩固自己所学的知识,不足之处欢迎指出!
ImageLoader
对于图片请求,是少不了对ImageLoader
的使用,它是一个图片装载器,对图片请求的管理。下面来看下使用方式:
|
|
它有两个参数分别为RequestQueue
与ImageCache
,对于第一个参数我们再熟悉不过了,至于第二个参数是为了构建图片缓存的,如不构建则缓存自然就失效了。现在我们进入ImageLoader
查看它的源码,看它到底做了什么?
|
|
进去发现它提供了一个get
方法,对图片的处理就是在这里,如果使用过Volley
图片加载的都知道我们都要调用它的get
的方法,那么它到底又做了什么呢?
get
|
|
根据上面的源码,它传递了几个参数,其中包括请求的url
、监听器ImageListener
、返回的图片宽高与缩放类型,ImageListener
是一个接口,是为了对响应不同处理的回调,包括onResponse
与onErrorResponse
。下面看主要代码,首先判断该方法调用是否在主线程,不是则抛出异常,对于对主线程的判断应该很容易想到,因为要对请求的响应做处理更新图片等,对UI
的操作都要在主线程,所以自然要在主线程进行更新。下一步都是一贯的作风,(10
行代码)先判断缓存中是否存在该请求的Bitmap
,如果存在则构造一个ImageContainer
,将必要信息传入,这是一个容器用来保存图片的信息,包括
Bitmap
请求的图片ImageListener
监听器,包括后续回调的onResponse
与onErrorResponse
方法mCacheKey
缓存的key
mRequestUrl
请求的url
然后调用imageListener
的onResponse
方法。不存在与会为其创建一个ImageContainer
为了保存后续的Bitmpa
,同时我们会发现它也会先调用onResponse
这一步并不是设置加载的图片,而是设置默认正在加载的图片(如果你设置了的话)。(25
行代码)如果该请求正在被执行,将当前的ImageContainer
加入到一个LinkedList
中为le后续响应的递送;下面才是真在从网络上获取(34
行代码)构造了一个Request
并将其加入到了请求队列中,下面我们来看下这个makeImageRequest
实现了什么
makeImageRequest
|
|
现在明白了吧,其实就是使用了ImageRequest
,与StringRequest
类似,在上一篇文章Android Volley源码分析(一)已经讲了,不管是直接使用缓存还是网络请求,在最后都会将结果交由DeliveryResponse
进行递送,根据响应情况分别调用监听器的onResponse
与onErrorResponse
方法。那么其中的onGetImageSuccess
与onGetImageError
方法又做了什么呢?
onGetImageSuccess && onGetImageError
|
|
做的事无非就是将Bitmap
加入缓存中同时更新请求中的Bitmap
以便后续batchResponse
的批处理操作
|
|
onGetImageError
也类似,只不过是错误的响应无需加入到缓存中,但还是要进行batchResponse
,既然如此那我们继续进入batchResponse
源码。
batchResponse
|
|
通过代码发现其中有一个run
方法,里面根据图片容器ImageContainer
中的信息分别调用监听器中的onResponse
与onErrorResponse
方法,这就是我们前面get
中所传递的ImageListener
接口。其中方法的实现可以由我们自己来实现。最后通过mHandler
(作用在主线程)进行处理。
对于ImageListener
接口,也有默认的实现getImageListener
默认的getImageListener实现
|
|
在这里我们能看到我们最喜欢看到的代码:对ImageView
进行图片填充。但我们只使用默认的实现可能会存在许多问题,例如多条目的图片加载会复用item
如果不对其进行特殊处理会很容易产生多图片加载时的闪烁与异位问题
。那要如何解决呢?其实我们要做的就是不让他直接就处理返回的结果,而是要进行特殊的判断,既然如此,我们只有自己实现接口中的方法,自己来实现业务逻辑。
实现自己的ImageListener接口
|
|
如代码中的注释所示,对View
其进行设置Tag
可以是url
。只有符合当前位置的url
链接的Bitmap
才能填充当前的View
,其实要完美解决是有一个前提的,设置了默认的显示图片,如果你不设置默认图片的显示,虽然异位的情况解决了,但可能还是会导致图片的闪烁。只不过现在图片加载都会设置默认的图片显示,所以基本就不存这种问题了。
ImageCache
关于缓存在ImageLoader
中提供了ImageLoader.ImageCache
接口,就两个方法getBitmap
与putBitmap
,如果细心的话前面对缓存的处理都是通过这两个方法来获取与保存的。所以要实现缓存的话就必须实现该接口,并实现其中的方法。不过对缓存的管理我们可以继承LruCache
算法来实现
|
|
对于缓存maxSize
的大小可以取内存阀值的1/8
|
|
NetWorkImageView
Volley
还提供了一个专门的网络图片加载的控件,可以直接将xml
文件中的ImageView
替换成NetWorkImageView
,该控件也是继承了ImageView
只不过它对专门对使用Volley
图片加载进行了封装。使用时通过setDefaultImageResId
与setErrorImageResId
设置默认与错误显示图片,调用则是直接setImageUrl
,还是来看下源码
|
|
参数url
与我们上面提到的ImageLoader
,说明不管使用哪种方式ImageLoader
都是必须的。再看下loadImageIfNecessary
做了什么?
|
|
代码比较多,但都是很容易理解的,前面一部分都是对宽度与高度的计算,使图片能适应控件的大小。我们先来找到我们所熟悉的(48
行代码),它就是前面的get
方法以及ImageListener
的实现,这里相信现在不用多说了,它调用的源码都在前面讲了。再向上走(32
行代码)发现没,它将当前NetWorkImageView
中的ImageContainer
中保存的url
与请求的url
相比较,是不是与前面所说的防闪烁与异位
的处理相似呢?你看,如果url
相同的话说明当前控件上所显示的图片与结果吻合,就直接返回不做处理;否则mImageContainer.cancelRequest()
取消原来的请求,并且setDefaultImageOrNull()
,该方法的作用看方法名就知道:如果设置了默认图片就显示默认图片,没有就设置空图片不显示。后面的处理就是上面的get
处理了,再进行网络请求。所以NetWorkImageView
使用起来相对于更简单,更容易上手。
吐槽
其实上面的两种方式加载图片最终都是调用的ImageRequest
,而ImageRequest
是继承与Request
,所以还是走到了前面那篇文章所讲的内容。同理再去看StringRequest
、JsonObjectRequest
与JsonArrayRequest
都是类似的,都是重写deliverResponse
与parseNetworkResponse
方法实现不同数据类型的请求。同样这两个方法又回到了Android Volley源码分析(一)中所分析的内容。
总结
这里对Volley
加载图片的使用进行简单的概括
- 创建
RequestQueue
- 创建
ImageLoader
- 实现缓存。继承
LruCache
实现ImageLoader
的ImageLoader.ImageCache
接口 - 如果使用
ImageView
则实现自己的ImageLoader.ImageListener
,调用get
方法 - 如果使用
NetWorkImageView
则直接调用setImageUrl
方法