public NetworkResponse performRequest(Request> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
Map responseHeaders = Collections.emptyMap();
try {
// Gather headers.
Map headers = new HashMap();
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.
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);
}
}
}
}
1、首先创建循环机制,使请求失败可以再次请求(3行);
2、添加头部信息(9 - 10行);
3、调用HttpStack对象进行网络请求,进入网络请求的第三层(11行);
4、获取请求结果的状态(12 - 13行);
5、更新头部信息(14行);
6、如果获取的状态行是304则说明之前已获取过数据且服务器未做修改,直接返回上次请求的数据(16 - 32行);
7、将数据内容写入到responseContents里(35 - 41行);
8、请求完成,如果访问总时长超过3秒将访问的详细信息打印出来(44 - 45行);
9、如果状态码返回是异常,报IO异常,跳转到下面的catch中(47 - 49);
10、如果是连接异常则重新连接(53、55行);
11、如果超时,则跳出循环向上抛异常,超时时间最大值可设置(56行);
12、处理IO异常,如果是因为访问无权限或禁止访问这种认证问题,可以再给一次机会,否则直接抛异常(59 - 80行);
多次网络请求机制
BasicNetwork.java
接下来我们来看一下Volley多次访问逻辑,入口为attemptRetryOnException()方法。
private static void attemptRetryOnException(String logPrefix, Request> request,
VolleyError exception) throws VolleyError {
RetryPolicy retryPolicy = request.getRetryPolicy();
int oldTimeout = request.getTimeoutMs();
try {
retryPolicy.retry(exception);
} catch (VolleyError e) {
request.addMarker(
String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));
throw e;
}
request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
}
1、获取Request对象里面的RetryPolicy对象,并获取超时时间(3 - 4行);
2、调用RetryPolicy对象的retry()方法来检测是否需要重新访问(7行);
向下看retry()的源码
DefaultRetryPolicy.java
public void retry(VolleyError error) throws VolleyError {
mCurrentRetryCount++;
mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
if (!hasAttemptRemaining()) {
throw error;
}
}
1、重新访问次数加一(2行);
2、累加超时时间(3行);
3、判断重新访问次数是否大于最大访问次数(默认是1,可自定义),大于最大值报异常跳出循环(4行);
封装HttpURLConnection实现网络连接(HttpClient暂不整理)
如果是Android2.3即以上版本调用上面HttpStack对象的performRequest()方法的会映射到HurlStack类里面的performRequest()方法来实现网络连接。
HurlStack.java
public HttpResponse performRequest(Request> request, Map additionalHeaders)
throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap map = new HashMap();
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 connection = openConnection(parsedUrl, request);
for (String headerName : map.keySet()) {
connection.addRequestProperty(headerName, map.get(headerName));
}
setConnectionParametersForRequest(connection, request);
// Initialize HttpResponse with data from the HttpURLConnection.
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
int responseCode = 0;
try {
responseCode = connection.getResponseCode();
} catch (IOException e) {
connection.getErrorStream();
responseCode = connection.getResponseCode();
}
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.");
}
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
response.setEntity(entityFromConnection(connection));
for (Entry> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h);
}
}
return response;
1、得到URL并获取头部信息(3 - 6行);
2、mUrlRewriter是干什么的?mUrlRewriter默认为null,但如果赋值的话,所有使用到的网络请求URL统一替换成mUrlRewriter中的URL,应该是项目测试用的(7 - 13行);
3、创建HttpURLConnection对象,并做一些连接前的配置,看一下这个方法的细节(15行);
调用createConnection()方法创建一个HttpURLConnection对象,createConnection()方法里面只是判断了一下是不是通过https访问。如果是https则在
HttpsURLConnection这个类里面的两个静态对象初始化,
如果是http的话不做处理。因为如果是http的话后面的执行直接用connection这个引用就可以,如果是https的话由于已经在
HttpsURLConnection这个类中把必要的静态数据初始化了所以可以直接将connection强行转换(170行、179 - 181行);
4、设置头部(16 - 18行);
5、设置连接参数,支持8种请求方式(19行);
6、获取连接最终状态,如果是-1说明不能获取到,向上抛异常(22 - 34行);
7、配置BasicHttpResponse对象以便在最后返回它(35 - 37行);
8、将body写入到BasicHttpResponse对象中(38行);
9、将头部信息写入到BasicHttpResponse对象中(39 - 44行);
这样整体的网络结构就差不多了。如果继续深入可以研究上传功能的源码和HttpUrlConnect的源码。