Volley-Request的源码解析
前言
Request
Request
继承关系
Request请求接口结构如下所示:
![此处输入图片的描述][2]
- Request:用来构造一个请求对象
- StringRequest :响应的主体为字符串
- JsonRequest
:发送和接收JSON相关的接口 - JsonObjectRequest:发送和接收JSON对象
- JsonArrayRequest :发送和接收JSON数组
- ImageRequest :发送和接收Image
- ClearCacheRequest: 一个模拟的用来清理缓存的请求
源码分析
成员变量
接下来对Request的源码进行解析,首先看成员变量:
1 | public abstract class Request<T> implements Comparable<Request<T>> { |
DEFAULT_PARAMS_ENCODING = “UTF-8” 默认编码
mMethod 请求方式
mUrl 请求地址
mDefaultTrafficStatsTag 流量统计标签
mErrorListener 错误监听器
mSequence 请求序列号,就是这个request在队列中的序号(不是顺序),用于fifo算法
mRequestQueue 请求所在的请求队列
mShouldCache 是否使用缓存响应请求或者是否允许请求响应被缓存
mCanceled 该请求是否被取消
mResponseDelivered 该请求的结果是否已经被分发
mRetryPolicy 请求重试策略
mCacheEntry 缓存记录,这个记录用于缓存响应头等
mTag 用于自定义标记,可以理解为用于请求的分类 撤销请求的时候会用到
构造方法
1 | //根据请求方式,创建新的请求(需要地址,错误监听器等参数) |
1 | public Request<?> setRetryPolicy(RetryPolicy retryPolicy) { |
首先是请求方式,请求地址的设定,这是作为一个请求必须有的。然后是监听器的设定,注意这里只是设置了ErrorListner,说明errorListener是必须的,但是正确响应,我们有可能不处理。这样设定是合理的,因为出错了,我们必须处理,至于请求成功,我们可以不处理。那么我们想处理成功的请求怎么办呢,这需要在子类中重写构造方法(例如StringRequest)。
1 | private final Listener<String> mListener; |
接着是设置重试策略,关于重试策略,建议阅读:[Volley的请求重试策略相关源码分析][3],接下来是流量标志的设置,所谓流量标志,是用于调试日志记录的,不是重点。
finish方法
1 | void finish(final String tag) { |
finish方法,主要是做了一些日志记录的工作,最重要的是调用了mRequestQueue的finish()方法,来从队列中去除这个请求,关于具体的细节,建议阅读:中对RequestQueue的finish方法的介绍。
compareTo方法
Request
1 | //优先级枚举类 |
在Volley中,一个Request是放在优先级阻塞队列中的,由阻塞线程和请求线程从队列中取出Request进行处理,排在队列前面的Request会优先被处理。有的请求比较重要,希望早点执行,我们可以让它排在请求队列的前头。通过比较方法,我们就可以设定请求在请求队列中排队顺序的根据,从而让优先级高的排在前面。
Request中提供了getPriority方法,默认返回Priority.NORMAL,如果我们想要修改Request的优先级,在创建Request的时候直接重写Request的这个方法即可,如下所示。
1 | StringRequest stringRequest = new StringRequest(1,"",null,null){ |
响应处理方法
1 | //解析响应 |
parseNetworkError和deliverError用于相应失败的情况,parseNetworkError()用于解析Volleyerror,deliverError()方法回调了前面提到的ErrorListener。
parseNetworkError()是在[NetworkDispatcher][4]的run方法中调用的,deliverError()是在[ExecutorDelivery][5]中调用的。
parseNetworkResponse和deliverResponse用于处理相应成功的情况,parseNetworkResponse(NetworkResponse response)用于将网络response解析为本地response,解析出来的response,会交给deliverResponse(T response)方法。
parseNetworkResponse()是在[NetworkDispatcher][6]的run方法中调用的,deliverResponse()是在[ExecutorDelivery][7]中调用的。
关于为什么要解析,其实开头已经说过,要将结果解析为T类型。Volley中提供的各种Request都重写了这这个方法,将结果解析成自己定义的类型。
getHeaders()方法
1 | public Map<String, String> getHeaders() throws AuthFailureError { |
返回包含首部信息的Map,key为首部字段,value为首部字段的值,默认返回为空Map,如果用户想要设置某些请求首部,创建Request的时候,重写这个方法即可。
1 | StringRequest stringRequest = new StringRequest(1,"",null,null){ |
设置在Map中的首部信息,会在[HttpStack][8]的实现类的performRequest方法中,被取出,设置给网络请求。
1 | @Override |
post相关方法
我们可以设置Request为各种http请求方式,最常见的是get方式和post方式。通过get方式请求数据的时候,数据是通过组拼url提交的,也就是说如果我们的Request为get请求方式,我们直接将数据附加在url上即可。但是当我通过post方式提交数据的时候,数据是被添加在请求体中提交的,所以如果我们想要设置Request为post请求方式,我们需要重写getParams()方法,将我们的参数附加在map中:
1 | StringRequest stringRequest = new StringRequest(1,"",null,null){ |
我们通过getParams()返回的含有的参数值的Map最终也是在[HttpStack][9]的实现类的performRequest方法中,被取出,设置给网络请求。
1 | @Override |
设置的操作,就发生在[HttpStack][10]的实现类的setConnectionParametersForRequest方法中:
1 | static void setConnectionParametersForRequest(HttpURLConnection connection, |
我们可以看到当Request位post请求方式的时候,会执行如下代码:
1 | connection.setRequestMethod("POST"); |
设置HttpUrlConnection的请求方式为post请求方式,同时将提交的数据设置到HttpUrlConnection中,设置的最终操作就是[HttpStack][11]的实现类的addBodyIfExists方法中:
1 | private static void addBodyIfExists(HttpURLConnection connection, Request<?> request) |
在addBodyIfExists方法中,会调用Request的getBody()方法,getBody()中调用了我们重写的Request的getParams方法,获取到了含有我们要提交的数据的map。
1 | Request中的方法: |
getBody方法中取出我们要提交的数据之后,会按照我们设置的编码方式,将数据进行编码。
Request中的方法:
private byte[] encodeParameters(Map<String, String> params, String paramsEncoding) {
StringBuilder encodedParams = new StringBuilder();
try {
for (Map.Entry<String, String> entry : params.entrySet()) {
encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding));
encodedParams.append('=');
encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding));
encodedParams.append('&');
}
return encodedParams.toString().getBytes(paramsEncoding);
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee);
}
}
1
继续回到[HttpStack][12]的实现类的addBodyIfExists方法:
private static final String HEADER_CONTENT_TYPE = "Content-Type";
private static void addBodyIfExists(HttpURLConnection connection, Request<?> request)
throws IOException, AuthFailureError {
byte[] body = request.getBody();
if (body != null) {
connection.setDoOutput(true);
connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(body);
out.close();
}
}
1
得到按照特定的编码方式编码的要提交的数据的字节数组之后,通过输出流将数据提到到服务器,在提交之前,设置了请求的 "Content-Type"首部字段:
Request中的方法:
public String getBodyContentType() {
return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();
}
1
2
3
这样,设置Request为post方式提交数据的时候,Request的几个方法是如何调用的,我们已经很清楚了。
最后再看下当我们不设置Request的请求方式的时候,Request中的几个相关方法是如何调用的,当我们不设置Request的请求方式的时候,请求方式mMethod的值为:
Method.DEPRECATED_GET_OR_POST
1
我们在直接看[HttpStack][13]的实现类的setConnectionParametersForRequest方法,是如何处理的。
static void setConnectionParametersForRequest(HttpURLConnection connection,
Request<?> request) throws IOException, AuthFailureError {
switch (request.getMethod()) {
case Method.DEPRECATED_GET_OR_POST:
byte[] postBody = request.getPostBody();
if (postBody != null) {
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.addRequestProperty(HEADER_CONTENT_TYPE,
request.getPostBodyContentType());
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(postBody);
out.close();
}
break;
....
default:
throw new IllegalStateException("Unknown method type.");
}
}
1
当请求方式为Method.DEPRECATED_GET_OR_POST的时候,先调用Request的getPostBody()方法,检查有没有要提交的数据,如果没有数据的话,则认为这个请求方式是get方法,注意我们看到这里并没有执行设置connection为get请求方式,
connection.setRequestMethod("GET");
1
这是因为HttpUrlConnection默认就是get方式的,所以这里并没有进行设置。如果有提交的数据的话,则和前面处理方式一样,通过输出流将数据写到服务器,在提交之前,同样设置了请求的"Content-Type"首部字段,只不过这里调用的是getPostBodyContentType(),其实最终调用的还是getBodyContentType()方法,我们看下getPostBodyContentType()的源码:
Request中的方法:
@Deprecated
public String getPostBodyContentType() {
return getBodyContentType();
}
1
最后看下getPostBody()的实现,getPostBody()的源码如下所示:
@Deprecated
public byte[] getPostBody() throws AuthFailureError {
Map<String, String> postParams = getPostParams();
if (postParams != null && postParams.size() > 0) {
return encodeParameters(postParams, getPostParamsEncoding());
}
return null;
}
1
getPostBody()中调用了getPostParams()方法,getPostParams()的源码如下所示:
@Deprecated
protected Map<String, String> getPostParams() throws AuthFailureError {
return getParams();
}
1
2
3
getPostParams()最终调用了getParams()方法。
在对数据进行编码的encodeParameters方法中,调用了getPostParamsEncoding()方法,getPostParamsEncoding()的源码如下所示:
@Deprecated
protected String getPostParamsEncoding() {
return getParamsEncoding()。;
}
```
getPostParamsEncoding()最终是调用了getParamsEncoding()方法。
至此,关于Request的源码分析就结束了,撒欢。
参考链接:
[详细解读Volley(一)—— 基本Request对象 & RequestQueue][15]
[Volley库源码解析----主要是Request][16]
[Volley学习(三)ImageRequest、ImageLoader、NetworkImageView源码简读][17]
[volley源码解析(二)--Request<T>类的介绍(只介绍了Request<T>类,参考完)][18]
[Volley源码解析<三> Request请求][19]
[1]: http://thinkerzhangyan.com/2018/05/03/Volley-ResponseDelivery%E5%8F%8A%E5%85%B6%E5%AE%9E%E7%8E%B0%E7%B1%BB%E7%9A%84%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/
[2]: http://www.thinkerzhangyan.com/request.png
[3]: http://thinkerzhangyan.com/2018/05/03/Volley%E7%9A%84%E8%AF%B7%E6%B1%82%E9%87%8D%E8%AF%95%E7%AD%96%E7%95%A5%E7%9B%B8%E5%85%B3%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/
[4]: http://thinkerzhangyan.com/2018/05/03/Volley-NetworkDispatcher%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/
[5]: http://thinkerzhangyan.com/2018/05/03/Volley-ResponseDelivery%E5%8F%8A%E5%85%B6%E5%AE%9E%E7%8E%B0%E7%B1%BB%E7%9A%84%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/
[6]: http://thinkerzhangyan.com/2018/05/03/Volley-NetworkDispatcher%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/
[7]: http://thinkerzhangyan.com/2018/05/03/Volley-ResponseDelivery%E5%8F%8A%E5%85%B6%E5%AE%9E%E7%8E%B0%E7%B1%BB%E7%9A%84%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/
[8]: http://thinkerzhangyan.com/2018/05/02/HttpStack%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/
[9]: http://thinkerzhangyan.com/2018/05/02/HttpStack%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/
[10]: http://thinkerzhangyan.com/2018/05/02/HttpStack%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/
[11]: http://thinkerzhangyan.com/2018/05/02/HttpStack%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/
[12]: http://thinkerzhangyan.com/2018/05/02/HttpStack%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/
[13]: http://thinkerzhangyan.com/2018/05/02/HttpStack%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/
[15]: https://www.cnblogs.com/tianzhijiexian/p/4255488.html
[16]: https://blog.lixplor.com/2016/10/02/android-volley-analyze/
[17]: http://www.voidcn.com/article/p-zaqijwfw-bhr.html
[18]: https://blog.csdn.net/crazy__chen/article/details/46486123
[19]: https://blog.csdn.net/fenggit/article/details/50725435