request的默认编码方式是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;
}
request的全局变量:
//网络交互的方式
private final int mMethod;
/** URL of this request. */
private final String mUrl;
//报错时的回调方法
private final Response.ErrorListener mErrorListener;
//request的编号,用于执行先进先出的顺序
private Integer mSequence;
//request的队列
private RequestQueue mRequestQueue;
//request所对应的response是否应该被缓存
private boolean mShouldCache = true;
//该request是否被取消了
private boolean mCanceled = false;
//request所对应的response是否到达
private boolean mResponseDelivered = false;
//阙值,当超过此值时须在log中打印出来
private static final long SLOW_REQUEST_THRESHOLD_MS = 3000;
//重试策略
private RetryPolicy mRetryPolicy;
//当request可以从缓存中恢复但是又必须在网络中刷新时,缓存将在此保存,当返回的状态码为304时,这代表不必将它从缓存中驱逐
private Cache.Entry mCacheEntry = null;
//一个标记,用于批量取消request
private Object mTag;
接下来看一下他的一些方法:
//返回request交互的方式
public int getMethod() {
return mMethod;
}
//可以调用cancelAll取消所有带同一tag的request
public Request<?> setTag(Object tag) {
mTag = tag;
return this;
}
//为request设置重试策略
public Request<?> setRetryPolicy(RetryPolicy retryPolicy) {
mRetryPolicy = retryPolicy;
return this;
}
//通知消息队列该request已经完成。
void finish(final String tag) {
if (mRequestQueue != null) {
mRequestQueue.finish(this);
}
.
.
.
.
}
//被RequestQueue调用,为request设置编号
public final Request<?> setSequence(int sequence) {
mSequence = sequence;
return this;
}
/**
* Returns the URL of this request.
*/
public String getUrl() {
return mUrl;
}
//返回request对应的缓存键,默认情况下为url。
public String getCacheKey() {
return getUrl();
}
//取消该任务
public void cancel() {
mCanceled = true;
}
//返回额外的HTTP请求头。
public Map<String, String> getHeaders() throws AuthFailureError {
return Collections.emptyMap();
}
//返回用于POST或PUT的键值对参数。或者可以直接重写getBody(),传输自定义的数据。
protected Map<String, String> getParams() throws AuthFailureError {
return null;
}
//编码方式。用于转换从getParams得到的参数。并将其传入POST或PUT的内容content中
protected String getParamsEncoding() {
return DEFAULT_PARAMS_ENCODING;
}
//返回POST或PUT的content类型
public String getBodyContentType() {
return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();
}
//返回POST或PUT即将发送的内容。默认情况下,内容由通过application/x-www-form-urlencoded编码的params组成。当重写该方法时,务必考虑是否要重写getBodyContentType
public byte[] getBody() throws AuthFailureError {
Map<String, String> params = getParams();
if (params != null && params.size() > 0) {
return encodeParameters(params, getParamsEncoding());
}
return null;
}
//将params按照application/x-www-form-urlencoded的编码方式转换
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);
}
}
/**
* Returns the socket timeout in milliseconds per retry attempt. (This value can be changed
* per retry attempt if a backoff is specified via backoffTimeout()). If there are no retry
* attempts remaining, this will cause delivery of a {@link TimeoutError} error.
*/
public final int getTimeoutMs() {
return mRetryPolicy.getCurrentTimeout();
}
//将此request标记为response已返回
public void markDelivered() {
mResponseDelivered = true;
}
//子类必须复写该方法解析网络交互返回来的数据并return正确的数据类型。此方法将会在工作线程中调用。如果return null,response将不会被发送。
abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
//子类必须复写该方法解析网络错误。
protected VolleyError parseNetworkError(VolleyError volleyError) {
return volleyError;
}
//子类必须复写该方法执行发送已经被解析好的数据。
abstract protected void deliverResponse(T response);
接下来讲解一下”重试策略“RetryPolicy。该接口只有三个方法,该接口有一个实现类:DefaultRetryPolicy。默认重试次数为1次,超时时间为2.5秒
//返回当前超时总时间
public int getCurrentTimeout();
//返回当前重试次数
public int getCurrentRetryCount();
//申请超时补偿,准备下一次重试
public void retry(VolleyError error) throws VolleyError;
在NetworkDispatcher中可以看到,request的发送是通过调用BasicNetwork.performRequest(request),而该方法其实是在内部调用了HrulStack.performRequest(request)。那么接下来我们看一下HrulStack的调用过程。
@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);
if (mUrlRewriter != null) {
String rewritten = mUrlRewriter.rewriteUrl(url);
if (rewritten == null) {
throw new IOException("URL blocked by rewriter: " + url);
}
url = rewritten;
}
URL parsedUrl = new URL(url);
//配置HttpURLConnection的连接超时时间,读取超时时间等等
HttpURLConnection connection = openConnection(parsedUrl, request);
//将给定属性添加到请求头
for (String headerName : map.keySet()) {
connection.addRequestProperty(headerName, map.get(headerName));
}
//根据网络交互方式的不同将connection设置为GET或POST等等,其中如果交互方式为POST,PUT,那connection将调用request.getBody填充需发送的内容和request.getBosyContentType定义发送内容类型
setConnectionParametersForRequest(connection, request);
// Initialize HttpResponse with data from the HttpURLConnection.
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
int responseCode = connection.getResponseCode();
//返回的状态码为-1代表交互失败
if (responseCode == -1) {
// -1 is returned by getResponseCode() if the response code could not be retrieved.
// Signal to the caller that something was wrong with the connection.
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
}
//将HTTP协议文本,服务器发回的响应状态代码,状态码的文本描述传入
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
//将返回的消息内容,长度,类型,编码方式传入
response.setEntity(entityFromConnection(connection));
//填充响应头
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h);
}
}
return response;
}
接下来是BasicNetwork的performRequest.“重试策略”RetryPolicy便是在这里生效的。可以观察到的是该方法被while(true)包裹起来了。当发生SocketTimeoutException,ConnectTimeoutException,IOException(该错误视情况而重试)时,就会调用attemptRetryOnException(),该方法会调用request.getRetryPolicy(),进而调用RetryPolicy.retry进行重试。一旦重试次数超过设定的次数,就会抛出VolleyError。此外,这里还有部分关于Http状态码的判定,详细可参考:301、404、200、304等HTTP状态
@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
Map<String, String> responseHeaders = Collections.emptyMap();
try {
// Gather headers.
Map<String, String> headers = new HashMap<String, String>();
addCacheHeaders(headers, request.getCacheEntry());
httpResponse = mHttpStack.performRequest(request, headers);
StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode();
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
// Handle cache validation.
//状态码表示此次发送为第二次发送(即刷新),并且内容无改变时
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
Entry entry = request.getCacheEntry();
if (entry == null) {
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
// A HTTP 304 response does not have all header fields. We
// have to use the header fields from the cache entry plus
// the new ones from the response.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
entry.responseHeaders.putAll(responseHeaders);
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
entry.responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
// Some responses such as 204s do not have content. We must check.
//从HttpEntity中读取数据往byte[]中。其中方法entityToBytes()用的是ByteArrayOutputStream的变体PoolingByteArrayOutputStream,避免了多次分配内存,可自动根据内容大小扩充byte[].
if (httpResponse.getEntity() != null) {
responseContents = entityToBytes(httpResponse.getEntity());
} else {
// Add 0 byte response as a way of honestly representing a
// no-content request.
responseContents = new byte[0];
}
// if the request is slow, log it.
long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
logSlowRequests(requestLifetime, request, responseContents, statusLine);
if (statusCode < 200 || statusCode > 299) {
throw new IOException();
}
return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
SystemClock.elapsedRealtime() - requestStart);
} catch (SocketTimeoutException e) {
attemptRetryOnException("socket", request, new TimeoutError());
} catch (ConnectTimeoutException e) {
attemptRetryOnException("connection", request, new TimeoutError());
} catch (MalformedURLException e) {
throw new RuntimeException("Bad URL " + request.getUrl(), e);
} catch (IOException e) {
int statusCode = 0;
NetworkResponse networkResponse = null;
if (httpResponse != null) {
statusCode = httpResponse.getStatusLine().getStatusCode();
} else {
throw new NoConnectionError(e);
}
VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
if (responseContents != null) {
networkResponse = new NetworkResponse(statusCode, responseContents,
responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
statusCode == HttpStatus.SC_FORBIDDEN) {
attemptRetryOnException("auth",
request, new AuthFailureError(networkResponse));
} else {
// TODO: Only throw ServerError for 5xx status codes.
throw new ServerError(networkResponse);
}
} else {
throw new NetworkError(networkResponse);
}
}
}
}
下面是介绍下BasicNetworkResponse,这个类主要用来接收数据。
//HTTP状态码
public final int statusCode;
//返回的数据
public final byte[] data;
//响应头信息
public final Map<String, String> headers;
//当状态码为304时为true
public final boolean notModified;
//网络交互总时间
public final long networkTimeMs;
在NetworkDispatcher中会调用request.parseNetworkResponse(networkResponse)解析数据。举个例子,json解析方法为:
String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers, "utf-8"));
parseCharaset会从响应头中获取解码方式,如果获取失败就会使用utf-8解码。
parseNetworkResponse最终会调用下面这个方法返回Response.第二个参数会将数据解析后放入Cache.Entry中,等下在NetworkDispatcher中会存入硬存中。关于数据的解析需要掌握的是响应头等的参数,详细可参考:Android系列之网络(二)—-HTTP请求头与响应头
Response.success(new JSONObject(jsonString),HttpHeaderParser.parseCacheHeaders(response));
如果有错误就会调用:
Response.error(new ParseError(e));
下面是response的全局变量。它的实例化方法只有两个,就是如上面所述。
//最终返回的数据类型,当为null时表明有error
public final T result;
//缓存元数据
public final Cache.Entry cacheEntry;
//错误信息
public final VolleyError error;
//当为true时,表明此次得到的数据为soft-expired,第二次刷新得到的数据将很快到达
public boolean intermediate = false;
到这里为止,数据已经处理完毕。接下来是通过回调将数据在主线程中处理。在RequestQueue接收的参数中有一个是ExecutorDelivery,这个类里面有有一个内部类ResponseDeliveryRunnable。
ExecutorDelivery会将数据封在ResponseDeliveryRunnable中,然后调用handler.post(runnable)在主线程中运行
public ExecutorDelivery(final Handler handler) {
// Make an Executor that just wraps the handler.
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
public ExecutorDelivery(Executor executor) {
mResponsePoster = executor;
}
@Override
public void postResponse(Request<?> request, Response<?> response) {
postResponse(request, response, null);
}
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
@Override
public void postError(Request<?> request, VolleyError error) {
request.addMarker("post-error");
Response<?> response = Response.error(error);
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
}
private class ResponseDeliveryRunnable implements Runnable {
private final Request mRequest;
private final Response mResponse;
private final Runnable mRunnable;
public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
mRequest = request;
mResponse = response;
mRunnable = runnable;
}
@SuppressWarnings("unchecked")
@Override
public void run() {
// If this request has canceled, finish it and don't deliver.
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
// Deliver a normal response or error, depending.
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
// If this is an intermediate response, add a marker, otherwise we're done
// and the request can be finished.
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
mRequest.finish("done");
}
//这句代码会在CacheDispatcher中运行,如果缓存已经Soft-expired,将会启动第二次刷新,此时CacheDispatcher将会调用
/*
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
mNetworkQueue.put(request);
} catch (InterruptedException e) {
// Not much we can do about this.
}
}
});
*/
if (mRunnable != null) {
mRunnable.run();
}
}
}
}