@946898963
2018-05-18T12:06:01.000000Z
字数 11776
阅读 1028
Android控件跟框架
Android源码分析
Request是一个抽象类,Request被称为请求,通过继承Request来自定义request,为volley提供了更加灵活的接口。
Request中的泛型T,是指解析response以后的结果。ResponseDelivery会把response分派给对应的request。在我们定义的请求中,需要重写parseNetworkResponse(NetworkResponse response)这个方法,解析请求,解析出来的结果,就是T类型的。
Request请求接口结构如下所示:
接下来对Request的源码进行解析,首先看成员变量:
public abstract class Request<T> implements Comparable<Request<T>> {
//默认编码
private static final String DEFAULT_PARAMS_ENCODING = "UTF-8";
//请求方式枚举类型
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;
}
//用于跟踪请求的生存时间,用于调试
private final MarkerLog mEventLog = MarkerLog.ENABLED ? new MarkerLog() : null;
//请求方式
private final int mMethod;
//请求URL
private final String mUrl;
//流量统计标签
private final int mDefaultTrafficStatsTag;
//错误监听器
private final Response.ErrorListener mErrorListener;
//请求序号,用于fifo算法
private Integer mSequence;
// 请求所在的请求队列
private RequestQueue mRequestQueue;
//是否使用缓存响应请求或者是否允许请求响应被缓存
private boolean mShouldCache = true;
//该请求是否被取消
private boolean mCanceled = false;
//该请求的结果是否已经被分发
private boolean mResponseDelivered = false;
//请求重试策略
private RetryPolicy mRetryPolicy;
//缓存记录。当请求可以从缓存中获得响应,但必须从网络上更新时。我们保留这个缓存记录,所以一旦从网络上获得的响应带有Not Modified (没有更新)时,来保证这个缓存没有被回收.
private Cache.Entry mCacheEntry = null;
//用于自定义标记,可以理解为用于请求的分类 撤销请求的时候会用到
private Object mTag;
....
}
DEFAULT_PARAMS_ENCODING = "UTF-8" 默认编码
mMethod 请求方式
mUrl 请求地址
mDefaultTrafficStatsTag 流量统计标签
mErrorListener 错误监听器
mSequence 请求序列号,就是这个request在队列中的序号(不是顺序),用于fifo算法
mRequestQueue 请求所在的请求队列
mShouldCache 是否使用缓存响应请求或者是否允许请求响应被缓存
mCanceled 该请求是否被取消
mResponseDelivered 该请求的结果是否已经被分发
mRetryPolicy 请求重试策略
mCacheEntry 缓存记录,这个记录用于缓存响应头等
mTag 用于自定义标记,可以理解为用于请求的分类 撤销请求的时候会用到
//根据请求方式,创建新的请求(需要地址,错误监听器等参数)
@Deprecated
public Request(String url, Response.ErrorListener listener) {
this(Method.DEPRECATED_GET_OR_POST, url, listener);
}
public Request(int method, String url, Response.ErrorListener listener) {
mMethod = method;
mUrl = url;
mErrorListener = listener;
setRetryPolicy(new DefaultRetryPolicy());
mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url);
}
public Request<?> setRetryPolicy(RetryPolicy retryPolicy) {
mRetryPolicy = retryPolicy;
return this;
}
首先是请求方式,请求地址的设定,这是作为一个请求必须有的。然后是监听器的设定,注意这里只是设置了ErrorListner,说明errorListener是必须的,但是正确响应,我们有可能不处理。这样设定是合理的,因为出错了,我们必须处理,至于请求成功,我们可以不处理。那么我们想处理成功的请求怎么办呢,这需要在子类中重写构造方法(例如StringRequest)。
private final Listener<String> mListener;
public StringRequest(int method, String url, Listener<String> listener,
ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
}
接着是设置重试策略,关于重试策略,建议阅读:Volley的请求重试策略相关源码分析,接下来是流量标志的设置,所谓流量标志,是用于调试日志记录的,不是重点。
void finish(final String tag) {
if (mRequestQueue != null) {
mRequestQueue.finish(this);
}
//调试相关,不是重点,暂时不用关心
if (MarkerLog.ENABLED) {
final long threadId = Thread.currentThread().getId();//线程id
if (Looper.myLooper() != Looper.getMainLooper()) {//请求不是在主线程
//如果我们不是在主线程记录log,我们需要在主线程做这项工作来保证正确的顺序
Handler mainThread = new Handler(Looper.getMainLooper());
mainThread.post(new Runnable() {
@Override
public void run() {
mEventLog.add(tag, threadId);
mEventLog.finish(this.toString());
}
});
return;
}
//如果在主线程,直接记录
mEventLog.add(tag, threadId);
mEventLog.finish(this.toString());
}
}
finish方法,主要是做了一些日志记录的工作,最重要的是调用了mRequestQueue的finish()方法,来从队列中去除这个请求,关于具体的细节,建议阅读:中对RequestQueue的finish方法的介绍。
Request继承了Comparable>接口,重写了compareTo()方法,用于对请求进行排列。
//优先级枚举类
public enum Priority {
LOW,
NORMAL,
HIGH,
IMMEDIATE
}
public Priority getPriority() {
return Priority.NORMAL;
}
@Override
public int compareTo(Request<T> other) {
Priority left = this.getPriority();
Priority right = other.getPriority();
//优先级越高,在请求队列中排得越前
//相同优先级的按照序号进行排列,序号越低,排得越前。
return left == right ?
this.mSequence - other.mSequence :
right.ordinal() - left.ordinal();
}
在Volley中,一个Request是放在优先级阻塞队列中的,由阻塞线程和请求线程从队列中取出Request进行处理,排在队列前面的Request会优先被处理。有的请求比较重要,希望早点执行,我们可以让它排在请求队列的前头。通过比较方法,我们就可以设定请求在请求队列中排队顺序的根据,从而让优先级高的排在前面。
Request中提供了getPriority方法,默认返回Priority.NORMAL,如果我们想要修改Request的优先级,在创建Request的时候直接重写Request的这个方法即可,如下所示。
StringRequest stringRequest = new StringRequest(1,"",null,null){
@Override
public Priority getPriority() {
return Priority.HIGH;
}
};
//解析响应
abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
//解析网络错误
protected VolleyError parseNetworkError(VolleyError volleyError) {
return volleyError;
}
//分发响应
abstract protected void deliverResponse(T response);
//分发网络错误
public void deliverError(VolleyError error) {
if (mErrorListener != null) {
mErrorListener.onErrorResponse(error);
}
}
parseNetworkError和deliverError用于相应失败的情况,parseNetworkError()用于解析Volleyerror,deliverError()方法回调了前面提到的ErrorListener。
parseNetworkError()是在NetworkDispatcher的run方法中调用的,deliverError()是在ExecutorDelivery中调用的。
parseNetworkResponse和deliverResponse用于处理相应成功的情况,parseNetworkResponse(NetworkResponse response)用于将网络response解析为本地response,解析出来的response,会交给deliverResponse(T response)方法。
parseNetworkResponse()是在NetworkDispatcher的run方法中调用的,deliverResponse()是在ExecutorDelivery中调用的。
关于为什么要解析,其实开头已经说过,要将结果解析为T类型。Volley中提供的各种Request都重写了这这个方法,将结果解析成自己定义的类型。
public Map<String, String> getHeaders() throws AuthFailureError {
return Collections.emptyMap();
}
返回包含首部信息的Map,key为首部字段,value为首部字段的值,默认返回为空Map,如果用户想要设置某些请求首部,创建Request的时候,重写这个方法即可。
StringRequest stringRequest = new StringRequest(1,"",null,null){
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = new HashMap<>();
return headers;
}
};
设置在Map中的首部信息,会在HttpStack的实现类的performRequest方法中,被取出,设置给网络请求。
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap<String, String> map = new HashMap<String, String>();
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
....
URL parsedUrl = new URL(url);
HttpURLConnection connection = openConnection(parsedUrl, request);
for (String headerName : map.keySet()) {
connection.addRequestProperty(headerName, map.get(headerName));
}
....
return response;
}
我们可以设置Request为各种http请求方式,最常见的是get方式和post方式。通过get方式请求数据的时候,数据是通过组拼url提交的,也就是说如果我们的Request为get请求方式,我们直接将数据附加在url上即可。但是当我通过post方式提交数据的时候,数据是被添加在请求体中提交的,所以如果我们想要设置Request为post请求方式,我们需要重写getParams()方法,将我们的参数附加在map中:
StringRequest stringRequest = new StringRequest(1,"",null,null){
@Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> params = new HashMap<>();
params.put("userName", "zhangsan");
params.put("password", "123");
return params;
}
};
我们通过getParams()返回的含有的参数值的Map最终也是在HttpStack的实现类的performRequest方法中,被取出,设置给网络请求。
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap<String, String> map = new HashMap<String, String>();
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
....
URL parsedUrl = new URL(url);
HttpURLConnection connection = openConnection(parsedUrl, request);
for (String headerName : map.keySet()) {
connection.addRequestProperty(headerName, map.get(headerName));
}
setConnectionParametersForRequest(connection, request);
....
return response;
}
设置的操作,就发生在HttpStack的实现类的setConnectionParametersForRequest方法中:
static void setConnectionParametersForRequest(HttpURLConnection connection,
Request<?> request) throws IOException, AuthFailureError {
switch (request.getMethod()) {
....
case Method.POST:
connection.setRequestMethod("POST");
addBodyIfExists(connection, request);
break;
....
default:
throw new IllegalStateException("Unknown method type.");
}
}
我们可以看到当Request位post请求方式的时候,会执行如下代码:
connection.setRequestMethod("POST");
addBodyIfExists(connection, request);
设置HttpUrlConnection的请求方式为post请求方式,同时将提交的数据设置到HttpUrlConnection中,设置的最终操作就是HttpStack的实现类的addBodyIfExists方法中:
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();
}
}
在addBodyIfExists方法中,会调用Request的getBody()方法,getBody()中调用了我们重写的Request的getParams方法,获取到了含有我们要提交的数据的map。
Request中的方法:
public byte[] getBody() throws AuthFailureError {
Map<String, String> params = getParams();
if (params != null && params.size() > 0) {
return encodeParameters(params, getParamsEncoding());
}
return null;
}
private static final String DEFAULT_PARAMS_ENCODING = "UTF-8";
protected String getParamsEncoding() {
return DEFAULT_PARAMS_ENCODING;
}
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);
}
}
继续回到HttpStack的实现类的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();
}
}
得到按照特定的编码方式编码的要提交的数据的字节数组之后,通过输出流将数据提到到服务器,在提交之前,设置了请求的 "Content-Type"首部字段:
Request中的方法:
public String getBodyContentType() {
return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();
}
这样,设置Request为post方式提交数据的时候,Request的几个方法是如何调用的,我们已经很清楚了。
最后再看下当我们不设置Request的请求方式的时候,Request中的几个相关方法是如何调用的,当我们不设置Request的请求方式的时候,请求方式mMethod的值为:
Method.DEPRECATED_GET_OR_POST
我们在直接看HttpStack的实现类的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.");
}
}
当请求方式为Method.DEPRECATED_GET_OR_POST的时候,先调用Request的getPostBody()方法,检查有没有要提交的数据,如果没有数据的话,则认为这个请求方式是get方法,注意我们看到这里并没有执行设置connection为get请求方式,
connection.setRequestMethod("GET");
这是因为HttpUrlConnection默认就是get方式的,所以这里并没有进行设置。如果有提交的数据的话,则和前面处理方式一样,通过输出流将数据写到服务器,在提交之前,同样设置了请求的"Content-Type"首部字段,只不过这里调用的是getPostBodyContentType(),其实最终调用的还是getBodyContentType()方法,我们看下getPostBodyContentType()的源码:
Request中的方法:
@Deprecated
public String getPostBodyContentType() {
return getBodyContentType();
}
最后看下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;
}
getPostBody()中调用了getPostParams()方法,getPostParams()的源码如下所示:
@Deprecated
protected Map<String, String> getPostParams() throws AuthFailureError {
return getParams();
}
getPostParams()最终调用了getParams()方法。
在对数据进行编码的encodeParameters方法中,调用了getPostParamsEncoding()方法,getPostParamsEncoding()的源码如下所示:
@Deprecated
protected String getPostParamsEncoding() {
return getParamsEncoding()。;
}
getPostParamsEncoding()最终是调用了getParamsEncoding()方法。
至此,关于Request的源码分析就结束了,撒欢。
参考链接:
详细解读Volley(一)—— 基本Request对象 & RequestQueue
Volley学习(三)ImageRequest、ImageLoader、NetworkImageView源码简读