基础知识
要弄明白超时重试策略首先明白两个异常信息:
java.net.SocketTimeoutException
This exception is thrown when a timeout expired on a socket read or accept operation.
当读取一个socket或者接受操作超时会抛出该异常,通俗讲就是响应超时。
org.apache.http.conn.ConnectTimeoutException
A timeout while connecting to an HTTP server or waiting for an available connection from an HttpConnectionManager.
当连接HTTP服务或者从HttpConnectionManager中得到一个可用的连接超时后抛出该异常,通俗讲就是请求超时。
个人对一次http请求的理解为以下三个阶段,一:建立连接;二:数据传送;三,断开连接;
当建立连接在规定的时间内(ConnectionTimeOut )没有完成,那么此次连接就结束,抛出ConnectTimeoutException后续的SocketTimeOutException就一定不会发生。
只有当连接建立起来后,也就是没有发生ConnectionTimeOutException ,才会开始传输数据,如果数据在规定的时间内(SocketTimeOut)传输完毕,则断开连接。否则,触发SocketTimeOutException
重试策略
Volley对超时重试策略的处理在BasicNetwork的performRequest()方法中,部分代码如下:
//这里的while循环进行重试操作的,往下执行的操作中如果有正确返回值或者抛出异常,则该循环结束
//一次请求只有发生SocketTimeoutException 或ConnectTimeoutException或认证失败的401、403错误才会进行重试策略
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 {
........省略部分代码.........
return response;
} 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);
}
}
}
}
attemptRetryOnException的代码如下:
/**
* Attempts to prepare the request for a retry. If there are no more attempts remaining in the
* request's retry policy, a timeout exception is thrown.
* @param request The request to use.
*/
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));
}
这段中如果执行retryPolicy.retry(exception);没有抛异常,则继续执行超时重试策略;在DefaultRetryPolicy的具体实现中:
/**
* Prepares for the next retry by applying a backoff to the timeout.
* @param error The error code of the last attempt.
*/
@Override
public void retry(VolleyError error) throws VolleyError {
mCurrentRetryCount++;
mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
if (!hasAttemptRemaining()) {
throw error;
}
}
/**
* Returns true if this policy has attempts remaining, false otherwise.
*/
protected boolean hasAttemptRemaining() {
return mCurrentRetryCount <= mMaxNumRetries;
}
从retry()方法中可以看出每次执行retry后,超时时间会增大,当重试次数大于mMaxNumRetries值后,抛出异常,结束该次请求;
小结:
DefaultRetryPolicy中的默认最大mMaxNumRetries为1,mCurrentTimeoutMs为2500毫秒,个人觉得超时时间有点短,可根据后台的业务处理所花费的时间对该值进行修改。在HttpClient的默认超时时间为10000毫秒,我们也可根据这个参考值设置默认超时时间为10000毫秒