转载地址剖析Volley请求多次的原理
网络线程NetWorkDispather 的run 方法里有一行代码:NetworkResponse networkResponse = mNetwork.performRequest(request);意思开始请求服务端,直到返回响应,进performRequest方法看看:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
public
NetworkResponse performRequest(Request<?> request)
throws
VolleyError {
long
requestStart = SystemClock.elapsedRealtime();
while
(
true
) {
HttpResponse httpResponse =
null
;
byte
[] responseContents =
null
;
Map<String, String> responseHeaders =
new
HashMap<String, String>();
try
{
// Gather headers.
Map<String, String> headers =
new
HashMap<String, String>();
addCacheHeaders(headers, request.getCacheEntry());
1
httpResponse = mHttpStack.performRequest(request, headers);
StatusLine statusLine = httpResponse.getStatusLine();
int
statusCode = statusLine.getStatusCode();
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
// Handle cache validation.
2
if
(statusCode == HttpStatus.SC_NOT_MODIFIED) {
return
new
NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
request.getCacheEntry() ==
null
?
null
: request.getCacheEntry().data,
responseHeaders,
true
);
}
// 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();
}
3
return
new
NetworkResponse(statusCode, responseContents, responseHeaders,
false
);
}
catch
(SocketTimeoutException e) {
4
attemptRetryOnException(
"socket"
, request,
new
TimeoutError());
}
catch
(ConnectTimeoutException e) {
5
attemptRetryOnException(
"connection"
, request,
new
TimeoutError());
}
catch
(MalformedURLException e) {
6
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());
7
if
(responseContents !=
null
) {
8
networkResponse =
new
NetworkResponse(statusCode, responseContents,
responseHeaders,
false
);
if
(statusCode == HttpStatus.SC_UNAUTHORIZED ||
statusCode == HttpStatus.SC_FORBIDDEN) {
9
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处传请求内容和请求头,http通信,直到返回一个HttpResponose对象,解析这个对象,2,3处返回结果。上述是正常的请求返回过程,如果这个过程发生了异常并且被捕获,执行重发或者抛出异常。4,5处看到调用的方法的签名可猜到尝试重新请求,这是捕获的异常都是超时异常。进到attempRetryException看看:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
private
static
void
attemptRetryOnException(String logPrefix, Request<?> request,
VolleyError exception)
throws
VolleyError {
RetryPolicy retryPolicy = request.getRetryPolicy();
int
oldTimeout = request.getTimeoutMs();
try
{
1
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处,进retry方法看看
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public
void
retry(VolleyError error)
throws
VolleyError {
mCurrentRetryCount++;
mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
1
if
(!hasAttemptRemaining()) {
throw
error;
}
}
/**
* Returns true if this policy has attempts remaining, false otherwise.
*/
protected
boolean
hasAttemptRemaining() {
2
return
mCurrentRetryCount <= mMaxNumRetries;
}
|
关键在hasAttempRemaining()方法,先mCurrentRetryCount++,然后和mMaxNumRetries比较,这里的逻辑的意思是当前尝试请求的次数大于最大请求数就抛出异常,这样逻辑回到performRequest(),接着跳出循环,返回到网络线程,有网络线程处理抛出的异常。如果当前尝试请求的次数小于最大请求数,不抛出异常,逻辑回到performRequest(),接着下一次循环,再次尝试请求服务。在performRequest()的6处,说明请求url有问题,可能不合法,直接抛出RuntimeException异常返回网络线程。在performRequest()的7处,说明此时抛出了IO异常,在responseContents不为空的情况下,再次构造响应实例networkResponse,如果是认证问题的话,再给重新请求的机会,逻辑就走到attempRetryException(),只不过要抛出的异常是authFailureException,除了这个情况,其他情况都抛出异常返回网络线程,处理异常。这样我们分析了请求多次的原理,但又有疑问,在这个请求多次怎么设置它的次数呢,在刚才对attempRetryException的分析中发现,判断次数是否超出的逻辑是在一个叫DefaultRetryPolicy的类里面实现的,在request可以直接传一个DefaultRetryPolicy的实例进去,具体的代码是
= JsonPostRequest(IMConfig.map).setRetryPolicy(DefaultRetryPolicy(DefaultRetryPolicy.* DefaultRetryPolicy.DefaultRetryPolicy.))
关注DefaultRetryPolicy的构造方法第二个参数,就是指定请求多少次的最大数。另外两个参数是计算超时时间的。