前言:
Volley框架有许多优秀的机制,例如,HTTP缓存策略,内存和磁盘缓存策略,重试策略,四个网络线程一个缓存线程策略。
这里,从源码,解读Volley重试机制。
Volley中,定义出一个重试的RetryPolicy接口:
/**
* Retry policy for a request.
*
* 用途:
* 1. 重试策略,一定时间,重新发起一个请求。
* 2. 获取当前时间,当前重试的请求个数
*/
public interface RetryPolicy {
/**
* 获取当前重试的时间.
*/
public int getCurrentTimeout();
/**
* 返回当前重试次数
*/
public int getCurrentRetryCount();
/**
* 当应用超时的时候,准备下一次的重试
* @param error 上一次尝试发生的异常
* @throws VolleyError 当尝试不能执行,会抛出异常
*/
public void retry(VolleyError error) throws VolleyError;
}
接下来,看RetryPolicy接口的实现类DefaultRetryPolicy。
/**
* Default retry policy for requests.
* 用途:
* 请求中默认的重试策略
* 时间,重试次数,回退的乘数
*/
public class DefaultRetryPolicy implements RetryPolicy {
/** 当前超时累计的时间(毫秒为单位) */
private int mCurrentTimeoutMs;
/** 当前重试次数 */
private int mCurrentRetryCount;
/** 最大重试次数 */
private final int mMaxNumRetries;
/** 超时的乘数因子 */
private final float mBackoffMultiplier;
/** 默认的重试一次的时间*/
public static final int DEFAULT_TIMEOUT_MS = 2500;
/** 默认的重试次数 */
public static final int DEFAULT_MAX_RETRIES = 1;
/**
* 默认的超时的乘数因子
*
* 当前的重试时间=上一次超时时间+(上一次的超时时间*乘数因子)
*
* 例如: 乘数因子为1 ,一次重试时间为2.5秒 ,最大的重试次数为2
* 第一次重试: 重试时间=2.5+2.5*1=5秒
* 第二次重试: 重试时间=5+5*1=10秒
* */
public static final float DEFAULT_BACKOFF_MULT = 1f;
/**
* 使用默认的重试策略
*/
public DefaultRetryPolicy() {
this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT);
}
/**
*
*
* 参数1:策略执行时间
* 参数2:在执行时间内,重试次数
* 参数3:回退的乘数。 当前时间+=(当前时间*参数3)
*
*/
public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {
mCurrentTimeoutMs = initialTimeoutMs;
mMaxNumRetries = maxNumRetries;
mBackoffMultiplier = backoffMultiplier;
}
/**
* 返回当前重试的累加时间.
*/
@Override
public int getCurrentTimeout() {
return mCurrentTimeoutMs;
}
/**
* 返回当前的重试次数,第几次重试
*/
@Override
public int getCurrentRetryCount() {
return mCurrentRetryCount;
}
/**
* 重试策略执行逻辑
*/
@Override
public void retry(VolleyError error) throws VolleyError {
//累加尝试次数
mCurrentRetryCount++;
//累加重试时间
mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
if (!hasAttemptRemaining()) {//当前的重试次数大于指定重试次数,抛出该异常
throw error;
}
}
/**
* 返回true ,则请求还有重试次数
*/
protected boolean hasAttemptRemaining() {
return mCurrentRetryCount <= mMaxNumRetries;
}
}
从以上代码可知: 重试时间=上次重试时间+上次重试时间*乘数因子。
设置了重试时间,但需要作用在Http请求上才有效。
接下来,看下重试策略如何作用在HttpUrlConnection的连接时间和响应时间。
找到HurlStack类,该类是执行HttpUrlConnection的逻辑操作类。
public class HurlStack implements HttpStack {
//...省略部分代码
/**
* 根据url中带有的协议,来开启一个带有参数的HttpURLConnection,或者HttpsURLConnection
*/
private HttpURLConnection openConnection(URL url, Request> request) throws IOException {
HttpURLConnection connection = createConnection(url);
//获取Request中的指定时间
int timeoutMs = request.getTimeoutMs();
//设置连接时间
connection.setConnectTimeout(timeoutMs);
//设置读取时间
connection.setReadTimeout(timeoutMs);
//...省略部分代码
return connection;
}
}
可以发现设置的响应时间和连接时间都是Request中获取的。
接下来,找到Request类:
public abstract class Request<T> implements Comparable<Request<T>> {
public Request(int method, String url, Response.ErrorListener listener) {
//....省略部分代码
setRetryPolicy(new DefaultRetryPolicy());
}
/**
* 设置重试策略
*/
public Request> setRetryPolicy(RetryPolicy retryPolicy) {
mRetryPolicy = retryPolicy;
return this;
}
/**
* 重试策略中的当前累加重试时间
*/
public final int getTimeoutMs() {
return mRetryPolicy.getCurrentTimeout();
}
}
发现,Request这个超类已经默认设置了重试策略。
以上代码只是实现了重试机制的时间组作用在Http请求上,但如何计算重试机制的逻辑并没有实现,接下来查看Volley如何计算重试次数。
找到BasicNetwork 类:for循环方式,累加重试
public class BasicNetwork implements Network {
/**
* 执行网络请求,返回响应数据
*
* @param request Request to process
* @return
* @throws VolleyError 执行网络请求,for循环的方式,执行重试策略。
*
* 若是执行成功或者重试策略执行完抛出异常,跳出for循环。
*/
@Override
public NetworkResponse performRequest(Request> request) throws VolleyError {
//引导后的毫秒数(包含睡眠花费的时间)
long requestStart = SystemClock.elapsedRealtime();
while (true) {
//执行Http请求,每次循环都执行最新的重试时间
mHttpStack.performRequest(request, headers);
//若是服务器返回状态码在小于200或者待遇299时,抛出一个异常
if (statusCode < 200 || statusCode > 299) {
throw new IOException();
}
return new NetworkResponse(statusCode, responseContents, responseHeaders, false);
} catch (SocketTimeoutException e) {
//执行重试策略
attemptRetryOnException("socket", request, new TimeoutError());
} catch (ConnectTimeoutException e) {
//执行重试策略
attemptRetryOnException("connection", request, new TimeoutError());
} 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);
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);
}
}
}
}
/**
* 执行,重试策略。
*
* 若是请求中已经没有更多的重试策略,抛出这次请求网络的异常。
*
* @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));
}
}
从以上代码可知: 每次执行Http请求都会捕捉SocketTimeoutException
和ConnectTimeoutException
和服务器验证的重试错误。当发生这几个异常的时候,会走attemptRetryOnException()
,执行一次重试操作,并没有跳出for循环。当执行完网络请求会return跳出,或者重试策略执行完throw异常跳出。
成功执行完Request请求或者抛出异常,后并没有停止执行,而是传递结果到监听器中。
找到 NetworkDispatcher类:
public class NetworkDispatcher extends Thread {
@Override
public void run() {
//设置线程优先级,这里是后台线程
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Request> request;
//while循环,从网络队列中获取要执行的请求。
while (true) {
try {
// 从网络队列中获取一个请求
request = mQueue.take();
} catch (InterruptedException e) {
//当队列中抛出一个异常,且程序需要关闭网络线程池,则停止该线程。
if (mQuit) {
return;
}
continue;
}
try {
//....省略部分代码
//在NetWork子类类中执行网络请求的操作,返回网络响应数据
NetworkResponse networkResponse = mNetwork.performRequest(request);
//....省略部分代码
//在网络线程中指向解析响应的数据
Response> response = request.parseNetworkResponse(networkResponse);
//....省略部分代码
//在ResponseDelivery类中回调请求和解析后响应数据
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
parseAndDeliverNetworkError(request, volleyError);
} catch (Exception e) {
VolleyLog.e(e, "Unhandled exception %s", e.toString());
mDelivery.postError(request, new VolleyError(e));
}
}
}
/**
* 解析,传递网络异常。
* @param request
* @param error
*/
private void parseAndDeliverNetworkError(Request> request, VolleyError error) {
error = request.parseNetworkError(error);
mDelivery.postError(request, error);
}
}
BaseNetWork执行完Request重试策略后。抛出的异常会被 NetworkDispatcher线程中捕捉到,然后通过ResponseDelivery执行在主线程中回调给监听器。执行成功后服务器返回的Response数据会被解析,解析的结果通过通过ResponseDelivery执行在主线程中回调给监听器。