在《Volley源码解析(一)》和《Volley源码解析(二)》中简单讲解了Volley的内部工作原理,一言以蔽之就是:创建请求Request对象,然后将请求对象放入队列RequestQueue中,开启线程NetworkDispatcher不断从线程中获取请求对象,然后执行请求并返回响应对象。具体的细节可参考上面两篇博文,在此不细说,关于Volley的使用可以参考其官方文档。
其实对于android来说,大部分网络请求的业务都是通过接口获取数据然后对UI进行渲染;对于Volley来说你可以在任意的线程(UI线程或者非UI线程)创建Request对象,但是通过NetworkDispatcher发起网络请求然后完成数据解析后,最终解析后的数据会传给UI线程(Main Thread)来使用。那么这个过程是怎么实现的呢?在NetWorkDispatcher这个thread的run方法的内部有如下一句代码(详见《Volley源码解析(二)》):
//主要是回调Listener接口,通知客户端获取数据
mDelivery.postResponse(request, response);
事实上这段代码就是将解析后的response对象交给客户端使用,确切的说是在将数据交给main thread使用,这个mDelivery是ResponseDelivery接口,其具体在Volley的具体体现是ExecutorDelivery。那么mDelivery是什么时候初始化的呢?
在初始化请求队列RequestQueue对象的时候会通过Volley.newRequestQueue(context)
几经辗转会调用:
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
可以看出mDelivery变量的具体实例就是ExecutorDelivery,且该对象包含一个Handler对象,到此为止Volley的工作总体思路就不难猜出了:
1、异步线程发起网络请求
2、将请求包换的数据通过handler发送给main thread
这样就Volley就保证了获取网络数据回调更新UI界面过程,关于handler的工作机制可以参考博主的《android消息处理机制详解》
下面就看看ExecutorDelivery的post方法都作了写什么?
private final Executor mResponsePoster;
public ExecutorDelivery(final Handler handler) {
// Make an Executor that just wraps the handler.
//该Executor只是将Runable经过hanler重新包装一下,
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
从上面代码来看ExecutorDelivery的作用就是将交给Executor对象的Runnable交给handler处理,所以现在就可以看看post方法都作了写什么:
@Override
public void postResponse(Request> request, Response> response) {
postResponse(request, response, null);
}
@Override
public void postResponse(Request> request, Response> response, Runnable runnable) {
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
上面代码很简单,就是将ResponseDeliveryRunnable交给mResponsePoster这个Executor,最终交给handler处理。所以我们来看看ResponseDeliveryRunnable都作了写什么:
public void run() {
//省略部分代码
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
//省略部分代码
}
上面的逻辑也很简单,就是将回调相关接口来通知客户端网络返回结果,如果失败的话回调ErrorListener接口,将错误原因交给客户端,成功则交给Request对象的子类来处理(因为deliverResponse是一个抽象方法)。以StringRequest对象来举例,其deliverResponse方法实现如下:
@Override
protected void deliverResponse(String response) {
if (mListener != null) {
mListener.onResponse(response);
}
}
所以如果如果以StringRequest请求对象对正确和错误的响应都感兴趣的话,可以如下构建StringRequest对象:
RequestQueue queue = Volley.newRequestQueue(this);
String url = "http://www.baidu.com";
//请求成功的接口
Response.Listener<String> requestSuccessListener = new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.i("VolleryTest","请求成功"+response);
}
};
//请求失败的接口
Response.ErrorListener errorListener = new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.i("VolleryTest","请求失败");
error.printStackTrace();
}
};
//构建请求对象:此处为get请求
StringRequest request = new StringRequest( url,requestSuccessListener,errorListener);
queue.add(request);
运行上述代码发现打印请求失败,也就是说走了ErrorListener,并且Volley丢给我一个内部的log:
Unexpected response code 302
奇哉怪也,使用okhttp请求百度首页并没有出现这个错误,于是追踪其源码,在BasicNetWork这个类中找到类原因(这个类具体干嘛的,参考博主的其他两篇volley博客,在此不细说):
if (statusCode < 200 || statusCode > 299) {
throw new IOException();
}
一目了然,Volley对于http的状态码作了判断,不满足200 =
VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
所以从对状态码的处理来看,OKhttp的处理比Volley理智多了,并没有一棒子打死。
Volley对http方法的支持
我们知道http支持get,post,put,delete等方法,那么volley又是支持哪些http的方法呢?查看Request代码可以发现volley对常用的http方法都作了支持:
/**
* Supported request methods.
*/
public interface Method {
int DEPRECATED_GET_OR_POST = -1;
int GET = 0;
int POST = 1;
int PUT = 2;
int DELETE = 3;
int HEAD = 4;
int OPTIONS = 5;
int TRACE = 6;
int PATCH = 7;
}
get请求不用说,下面来简单分析下post请求是怎么实现的,确切的收是分析post请求是怎么携带参数的。在HurlStack类中提供了一个setConnectionParametersForRequest方法:
static void setConnectionParametersForRequest(HttpURLConnection connection,
Request> request) {
switch (request.getMethod()) {
case Method.POST:
connection.setRequestMethod("POST");
addBodyIfExists(connection, request);
break;
}
}
如果是post请求,则会调用addBodyIfExists方法来组建参数(如果有参数的话):
static void addBodyIfExists(HttpURLConnection connection, Request> request) {
byte[] body = request.getBody();
//省略组建post参数的代码
}
addBodyIfExists的第一步是通过Request对象的getBody方法来获取post请求所需的参数,所以进度该方法看看是怎么回事儿:
public byte[] getBody(){
//通过getParams方法获取post请求参数
Map params = getParams();
if (params != null && params.size() > 0) {
return encodeParameters(params, getParamsEncoding());
}
return null;
}
简单明了,原来post,delete,等请求参数的构建是通过Request对象的getParams() 方法来实现的:
protected Map<String, String> getParams() {
return null;
}
该方法是一个protected方法,所以如果以StringReqeust 对象发起post请求的话,那么代码就该是如下所示 了:
new StringRequest(Request.Method.POST,url,requestSuccessListener,errorListener) {
@Override
protected Map<String, String> getParams() {
Map<String,String> postParms = new HashMap<>();
postParms.put("xxx","xxxx");
return postParms;
}
};
不过看到这儿我总感觉怪怪的,本来是设置参数,提供的却是 getParams方法,而不是set方法,从设计上来说多少有点不合理的感觉。OKhttp对http请求方法的封装从代码设计角度来说就优雅的多了,OKhttp有点严格将请求和请求体分成两个对象Request和RequestBody,有点模仿html form表单请求的味道,想要了解更多的话可以参考《Okhttp发起http请求概述》
到此为止volley系列源码分析就结束了,至于volley的缓存因为其实现原理也很简单,在博主的volley系列中就不做具体解说了