您的位置:首页 > 移动开发 > Android开发

Android中的volley_12_请求重试策略RetryPolicy和DefaultRetryPolicy

2015-06-06 08:31 501 查看
面相接口的编程思想已经深入到volley的骨髓中了,当学习完volley,就算没有别的收货,但面相接口的编程思想必定深深印在脑海中,从HttpStack、到Network、再到Request等都是利用的接口思想。今天所说的请求重试策略RetryPolicy依然遵循了此思想,不得不令人感叹!!!

其实,直到现在这一刻,才忽然意识到,似乎volley就是利用接口来搭建其骨架的,之前的时候知道volley内利用了大量的接口,但是自己也只是明白,但是没有什么直观的概念,对于volley源码的学习和研究已经有段时间了,对其各个部分,也有了直观的了解,现在想想在各个关键点用的都是接口,骨架使用接口搭建的,而volley也有一套默认的对这些接口的实现类,形成了骨架间的血肉。嘿嘿,跑远了。

请求重试策略,很高大上的样子,但其内部结构真的很简单,先看看最直观的源码:

/**
* Retry policy for a request.
* 请求的重新请求策略
*/
public interface RetryPolicy {

/**
* Returns the current timeout (used for logging).
* 获取当前请求用时(用于Log)
*/
public int getCurrentTimeout();

/**
* Returns the current retry count (used for logging).
* 已经重新请求了几次
*/
public int getCurrentRetryCount();

/**
* Prepares for the next retry by applying a backoff to the timeout.  确定是否重试,参数为这次异常的具体信息。在请求异常时此接口会被调用,可在此函数实现中抛出传入的异常表示停止重试。
* @param error The error code of the last attempt.
* @throws VolleyError In the event that the retry could not be performed (for example if we
* ran out of attempts), the passed in error is thrown.
*/
public void retry(VolleyError error) throws VolleyError;
}


接口就定义了三个方法:获取当前超时时间、获取重试次数和重试。前两个方法很直观,重要的是第三个方法public void retry(VolleyError error) throws VolleyError ,而对于第三个方法想要强调的是throws VolleyError 抛出异常,也就是说在retry内部会抛出异常,之前一个字面retry就认为是在该方法内部重新发出网络请求,其实不是这样的,在内部的真正操作是变更重试策略的属性,如超时时间和重试次数,当超过了重试策略设定的限定就会抛出异常。注意注意注意:retry并不是真正的去重新发出网络请求。请求的重试是在BasicNetwork内实现的。稍后会涉及到。

DefaultRetryPolicy,RetryPolicy的默认实现类,也是volley的默认的请求重试策略。

该类内部定义了重试策略的必备属性:

/** The current timeout in milliseconds. 当前超时时间*/
private int mCurrentTimeoutMs;

/** The current retry count. 当前重试次数*/
private int mCurrentRetryCount;

/** The maximum number of attempts. 最大重试次数*/
private final int mMaxNumRetries;

/** The backoff multiplier for for the policy. 表示每次重试之前的 timeout 该乘以的因子,每重试一次,超时时间就变化一次*/
private final float mBackoffMultiplier;

/** The default socket timeout in milliseconds 默认超时时间*/
public static final int DEFAULT_TIMEOUT_MS = 5000;

/** The default number of retries  默认最大重试次数*/
public static final int DEFAULT_MAX_RETRIES = 1;


DefaultRetryPolicy有两个构造方法,在创建实例时使用了默认的属性。

/**
* Constructs a new retry policy using the default timeouts.
* 构造时,使用了默认的几个参数
*/
public DefaultRetryPolicy() {
this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT);
}

/**
* Constructs a new retry policy.
* @param initialTimeoutMs The initial timeout for the policy.  当前超时时间
* @param maxNumRetries The maximum number of retries.		最大重试次数
* @param backoffMultiplier Backoff multiplier for the policy.
*/
public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {
mCurrentTimeoutMs = initialTimeoutMs;
mMaxNumRetries = maxNumRetries;
mBackoffMultiplier = backoffMultiplier;
}


再看另外两个方法:

/**
* 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 {
//重试次数+1
mCurrentRetryCount++;
//超时时间每次都变化,增加
mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
if (!hasAttemptRemaining()) {
//如果超过了最大重试次数就抛出异常
throw error;
}
}

/**
* Returns true if this policy has attempts remaining, false otherwise.
* 当前重试次数是否已经达到最大重试次数
*/
protected boolean hasAttemptRemaining() {
return mCurrentRetryCount <= mMaxNumRetries;
}


hasAttemptRemaining()方法返回的是,当前请求次数是否已经超过了最大的重试次数。retry()方法则是定义重试请求后属性的变化,如果超过了最大次数那么抛出异常。

到目前为止,好像除了规定了超时时间和重试次数,其他的跟请求重试也没什么关系。回顾下BasicNetwork内的方法:

/**
* 执行具体的网络请求 ,需要Volley的Request,返回的是可以被传递的响应
*/
@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
//这里使用while(true)的含义是:保证请求重试策略的执行。
//如果网络正常返回结果 那么直接return
//如果需要进行请求重试,就用到这里了,保证了可以进行请求重试
while (true) {
......
try {
...................
return new NetworkResponse(statusCode, responseContents, responseHeaders, false);
//如果发生超时,认证失败等错误,进行重试操作,直到成功、抛出异常(不满足重试策略等)结束
//当catch后没有执行上边的return  而当前又是一个while(true)循环,可以保证下面的请求重试的执行,是利用循环进行请求重试,请求重试策略只是记录重试的次数、超时 时间等内容。
} catch (SocketTimeoutException e) {
//当出现异常的时候,尝试进行请求重试
<span style="color:#ffcc66;"> </span><span style="background-color: rgb(255, 255, 102);">attemptRetryOnException("socket", request, new TimeoutError());</span>
} catch (ConnectTimeoutException e) {
//当出现异常的时候,尝试进行请求重试
<span style="background-color: rgb(255, 255, 102);"> attemptRetryOnException("connection", request, new TimeoutError());</span>
} catch (MalformedURLException e) {
//url不正常异常
throw new RuntimeException("Bad URL " + request.getUrl(), e);
} catch (IOException e) {
//当出现IO异常时,在try内读取数据体时,如果出现IO异常,那么捕获异常,继续完成创建NetworkResponse的过程
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) {
//请求重试策略
<span style="background-color: rgb(255, 255, 102);">attemptRetryOnException("auth",
request, new AuthFailureError(networkResponse));</span>
} else {
// TODO: Only throw ServerError for 5xx status codes.
throw new ServerError(networkResponse);
}
} else {
throw new NetworkError(networkResponse);
}
}
}
}


public NetworkResponse performRequest(Request<?> request)方法是执行网络请求的方法,那么理所当然的要进行请求重试也是在这里进行。如何进行请求重试,注意在方法的内部是用while(true)括起来的,也就是说如果该方法正常执行完毕或者抛出异常时,必然就跳出循环了,但是如果请求失败没有return并且在catch内也没有超过重试策略限定条件时,必然会while(true)下重新请求一次,这样就达到了重试的目的。

看一下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 {
//内部实现,重试次数+1  超时时间变化
//如果重试次数超过限定的最大次数,该方法抛出异常
retryPolicy.retry(exception);
} catch (VolleyError e) {
//当超过了最大重试次数,捕获到异常,给改请求添加标记 标记超时
request.addMarker(
String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));
//这里才是最重要的,当仍然可以进行重试的时候,不会执行到catche语句,但是当执行到catch语句的时候,表示已经不能进行重试了,就抛出异常  这样while(true)循环就断了
throw e;
}
//给请求添加标记,请求了多少次
request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
}


其实到这里为止,文章已经可以结束了,但是在写文章的过程中又有了新的领悟,关于策略的。

百度了下关于策略的定义:

策略,指计策;谋略。一般是指:1. 可以实现目标的方案集合;2. 根据形势发展而制定的行动方针和斗争方法;3. 有斗争艺术,能注意方式方法。

应用到程序开发中,应该是理解为:可以实现目标的方案集合。需要注意的是,策略本身并不会具体的去执行。

DefaultRetryPolicy是请求重试的策略,它规定了超时时间、超时时间的变化、请求重试次数、最大请求重试次数以及请求重试后的变化,但是DefaultRetryPolicy并没有去执行真正的网络重试请求,仅仅是规划了,真正的重试还是要到网络请求类中。

写到这里,想到了volley中的Request,也应该算是一种策略,Request中定义了跟网络请求相关的必备属性,如请求方式、超时时间、参数等,但是Request并不会去执行网络请求,真正执行网络请求的还是BasicNetwork。

策略,是方案。

demo下载地址:http://download.csdn.net/detail/vvzhouruifeng/8747599
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: