Volley网络框架分享

时间:2021-08-30 09:48:55

简 介

1 Volley是在2013年谷歌发布的一款快捷高效、轻量级的网络通信框架,里面封装了HttpURLConnection和HttpClient的通信细节。

2 优点:
自动调度网络请求;
非常适合去进行数据量不大,但通信频繁的网络操作;
扩展性强。

3 缺点:
不适合大数据量的网络操作,比如文件的下载。

4 Volley的中文翻译为“齐射”,下图是其在发布演讲上的配图,可以看到很多弓箭在发射,每一个弓箭代表一个网络请求,喻指Volley框架是非常适合数据量不大但频繁的网络通信。所以在Volley源码的注释上,可以看到很多hit(击中)、in flight(飞行)等字样,结合其背景,就很好理解了。

Volley网络框架分享

HTTP通信

1 方式
Android系统中主要提供两种方式来进行HTTP通信,HttpURLConnection和HttpClient。

2 HttpURLConnection
优点:轻量极、API比较简单、易使用、可扩展 。。。
缺点:在Android 2.2版本之前,HttpURLConnection一直存在着一些bug,不太稳定。
简单使用:
Volley网络框架分享

3 HttpClient
优点:API多、比较稳定、bug数量也很少。
缺点:API数量过多,导致难在不破坏兼容性的情况下对它进行升级和扩展。
简单使用:
Volley网络框架分享

4 推荐
在Android 2.2版本之前,HttpClient拥有较少的bug,因此使用它是最好的选择。
在Android 2.3版本及以后,HttpURLConnection则是最佳的选择。它的API简单,体积较小,因而非常适用于Android项目。

Volley对HTTP的封装

上面对HttpURLConnection和HttpClient只是简单的使用,在实际项目中,因为要考虑的场景很多,会有不同的请求参数,不同的请求方式,不同的响应结果等等,所以用起来还是稍微有点复杂,如果不进行适当封装的话,很容易就会写出不少重复代码。 但是,要做到性能、内存、结构、代码都很好的封装,很难,所以在项目中,如果涉及到网络请求,我们一般不会自己去写网络通信代码,而是直接使用一些开源的网络框架。

Volley中和网络通信相关的类图如下:
Volley网络框架分享

在Volley框架中:
如果Android SDK版本大于等于9,使用HurlStack这种网络请求方式,实际上HurlStack里面封装的就是HttpURLConnection方式。
如果Android SDK版本小于9,使用HttpClientStack这种网络请求方式,实际上HttpClientStack里面封装的就是HttpClient方式。

最后将网络请求方式注入到BasicNetwork类中, BasicNetwork属于Volley框架进行网络通信的接口类,这里使用了装饰者的设计模式。

自动调度请求

自动调度请求,是Volley框架的一个核心。
说起来很简单,类似 消息循环处理机制 。在Volley框架中,有1个网络请求队列(mNetworkQueue)和4个网络请求调度线程(NetworkDispatcher),这4个线程不断的从队列中获取请求,交由网络通信接口类(BasicNetwork)处理,并返回响应结果。

缓存机制

缓存机制,是Volley框架的另一个核心。
前面说过,Volley框架的优点是非常适合数据量不大,但通信频繁的网络操作,缺点在不适合大数据量的网络操作,原因就在这。

在Volley框架中,除了有1个网络请求队列(mNetworkQueue)和4个网络请求调度线程(NetworkDispatcher),还有1个缓存请求队列(mCacheQueue)和1个缓存请求调度线程(CacheDispatcher)。缓存调度线程不断的从缓存队列中取出请求处理。

和缓存相关的类图如下:
Volley网络框架分享

Entry是接口Cache的内部类,和请求响应结果相关的数据都缓存在这个数据结构里,并且记录在文件里。
缓存文件的路径是在/data/data/下,默认最大的缓存大小为5M。

处理请求流程

下面看下添加一个请求的处理流程(RequestQueue.add):
Volley网络框架分享

缓存线程的处理流程(CacheDispatcher):
Volley网络框架分享

网络线程的处理流程(NetworkDispatcher):
Volley网络框架分享

从上面的流程可以看出,封装网络通信的难点在于怎样用严密的逻辑去管理所有的请求。

使 用

第一步:创建一个RequestQueue对象。

RequestQueue mQueue = Volley.newRequestQueue(context);

在这里面创建并启动了上述的5个线程,Volley框架已经跑起来了。

注意RequestQueue的设计是高并发的,里面可以缓存所有的HTTP请求,然后按照一定的算法并发地发送出去,因此我们没有必要为每一次HTTP请求都创建一个RequestQueue对象,这是非常浪费资源的,基本上在一个应用上创建一个就可以了。

第二步:构建一个请求

Volley自带的请求类图如下:
Volley网络框架分享
1 以StringRequest为例:

StringRequest stringRequest = new StringRequest(Method.POST, url, listener, errorListener); 

第一个参数为请求方式,get /post/put/delete…..
第二个参数就是目标服务器的URL地址;
第三个参数是服务器响应成功的回调;
第四个参数是服务器响应失败的回调。
比如请求百度的网址:

Volley网络框架分享

2 在Volley框架中还有一个RequestFuture,

public class RequestFuture<T> implements Future<T>, Response.Listener<T>,
Response.ErrorListener {
}

它可以阻塞等待响应结果,在项目中一般用这个作为请求的回调器。

3 所有的请求类都需要实现父类Request类中的两个抽象方法:

abstract protected Response<T> parseNetworkResponse(NetworkResponse response);//解析服务器返回的响应数据
abstract protected void deliverResponse(T response); //传送结果

我们看下在StringRequest中是怎么实现的?

Volley网络框架分享

这两个方法是怎么被框架调用的?

首先看下parseNetworkResponse方法的调用:

不管是从缓存线程中得到缓存的响应结果还是从网络线程中得到服务器响应结果,我们都是得到了NetworkResponse对象,然后就调用了request.parseNetworkRespons()将NetworkResponse对象传到各自请求,各自处理,再返回Response结果。

再看deliverResponse(T response)方法的调用:

上面我们得到了Response结果,是直接将这个结果传给deliverResponse吗?
不是,这里介绍volley框架里的另一个对象:ResponseDelivery(响应传送器)
在构建RequestQueue对象时,框架为我们new了这样一个对象mDelivery
(ExecutorDelivery extends ResponseDelivery),并且传入了主线程的Handler。

Volley网络框架分享

所以在上面得到了Response结果后,我们通过响应传送器将结果传出去了,

mDelivery.postResponse(request, response);

在响应传送器中将response构造成Runnable,交由主线程的Handler处理。在Runnable里,调用了request.deliverResponse(response),将response对象传到各自请求,各自处理。

所以,parseNetworkResponse方法是在子线程中执行的,而deliverResponse是在主线程中执行的。

4 对于图片请求,除了ImageRequest外,Volley还提供另一种方式:ImageLoader。

ImageLoader也可以用于加载网络上的图片,并且它的内部也是使用ImageRequest来实现的,不过ImageLoader明显要比ImageRequest更加高效,因为它不仅可以帮我们对图片进行缓存,还可以过滤掉重复的链接,避免重复发送请求。

这里不细讲这个,有兴趣的同学可以查阅相关资料或源码去了解。

第三步:添加一个请求

mQueue.add(stringRequest); 

通过这三步,一个最基本的HTTP发送与响应的功能就完成了。

扩 展

定制自己的请求,继承Request< T >

定制请求对象比较常用的几个方面:
第一,泛型参数T,即我们希望请求返回的数据类型;
第二,提交的请求参数,通过复写父类Request中的getParams()方法,提供请求的表单参数。

protected Map<String, String> getParams() throws AuthFailureError {
return null;
}

第三,设置重试策略(请求失败,自动再次发起请求),一般在请求的构造器中设置,通过调用父类Request中的setRetryPolicy()方法。

public Request<?> setRetryPolicy(RetryPolicy retryPolicy) {
mRetryPolicy = retryPolicy;
return this;
}

一般用框架提供的默认策略,可以设置连接超时时间,重试次数。

public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {

}

简单的文件上传和下载

不使用缓存机制,自己处理文件的相关逻辑。通过调用父类Request中的setShouldCache(false)方法将一个请求设置为不缓存。默认请求都是可以缓存的。

public final Request<?> setShouldCache(boolean shouldCache) {  }

总 结

Volley的官方文档中附有一张Volley的工作流程图,现在看懂了这张图,上面所讲的内容你算是明白了。

Volley网络框架分享

其中蓝色部分代表主线程,绿色部分代表缓存线程,橙色部分代表网络线程。我们在主线程中调用RequestQueue的add()方法来添加一条网络请求,这条请求会先被加入到缓存队列当中,如果发现可以找到相应的缓存结果就直接读取缓存并解析,然后回调给主线程。如果在缓存中没有找到结果,则将这条请 求加入到网络请求队列中,然后处理发送HTTP请求,解析响应结果,写入缓存,并回调主线程。

后 话

1 okhttp
OkHttp 是 Square 公司开源的针对 Java 和 Android 程序,封装的一个高性能 http 请求库,所以它的职责跟 HttpUrlConnection 是一样的,支持 spdy、http 2.0、websocket ,支持同步、异步,而且 OkHttp 又封装了线程池,封装了数据转换,封装了参数使用、错误处理等,api 使用起来更加方便。可以把它理解成是一个封装之后的类似 HttpUrlConnection 的一个东西,但是你在使用的时候仍然需要自己再做一层封装,这样才能像使用一个框架一样更加顺手。

2 Retrofit
Retrofit 是 Square 公司出品的默认基于 OkHttp 封装的一套 RESTful 网络请求框架,不了解 RESTful 概念的不妨去搜索学习下,RESTful 可以说是目前流行的一套 api 设计的风格,并不是标准。Retrofit 的封装可以说是很强大,里面涉及到一堆的设计模式,你可以通过注解直接配置请求,你可以使用不同的 http 客户端,虽然默认是用 http ,可以使用不同 Json Converter 来序列化数据,同时提供对 RxJava 的支持,使用 Retrofit + OkHttp + RxJava + Dagger2 可以说是目前比较潮的一套框架,但是需要有比较高的门槛。

3 Volley VS OkHttp
毫无疑问 Volley 的优势在于封装的更好,而使用 OkHttp 你需要有足够的能力再进行一次封装。而 OkHttp 的优势在于性能更高,因为 OkHttp 基于 NIO 和 Okio ,所以性能上要比 Volley更快。

4 OkHttp VS Retrofit
毫无疑问,Retrofit 默认是基于 OkHttp 而做的封装,这点来说没有可比性,肯定首选 Retrofit。

5 Volley VS Retrofit
这两个库都做了非常不错的封装,但是 Retrofit 解耦的更彻底,尤其 Retrofit 2.0 出来,Jake 对之前 1.0 设计不合理的地方做了大量重构,职责更细分,而且 Retrofit 默认使用 OkHttp ,性能上也要比 Volley 占优势,再有如果你的项目如果采用了 RxJava ,那更该使用 Retrofit 。
目前最流行的网络框架:Retrofit + RxJava

6 总结
所以综上,如果以上三种网络库你都能熟练掌握,那么优先推荐使用 Retrofit ,前提是最好你们的后台 api 也能遵循 RESTful 的风格,其次如果你不想使用或者没能力掌握 Retrofit ,那么推荐使用 Volley ,毕竟 Volley 你不需要做过多的封装,当然如果你们需要上传大数据,那么不建议使用 Volley,否则你该采用 OkHttp 。