暮鼓集 行走集
原作于2017年01月02日
在发送网络请求时,常常会遇到超时、回应错误等情形,这时便需要考虑是否重发请求。Volley提供了一套机制,用于检查问题及重发请求,这个机制的核心是RetryPolicy接口。
RetryPolicy
public interface RetryPolicy {
/**
* Returns the current timeout (used for logging).
*/
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;
}
方法getCurrentTimeout()返回请求超时的值,以毫秒为单位。Volley的Network底层执行请求操作时,会将这个值用作请求的超时时间,当超时发生时,就会触发RetryPolicy。
方法getCurrentRetryCount()返回请求的当前重发计数,往往使用这个计数与预定义的最大重试次数比较,一旦达到最大重试次数,就会停止重发。
最为重要的是方法retry(VolleyError error),在请求出现问题时,会被触发。参数error可以为如下子类,可以用于区别不同的问题原因:
TimeoutError - 超时 AuthFailureError - 鉴权失败(回应为401或403) ServerError - 服务器问题(回应为5XX) NetworkError - 网络问题(未收到回应)
(关于Volley如何触发Retry,可以参考BasicNetwork类的performRequest方法。)
在retry中,应针对错误做具体的处理,处理完成后,如果需要重发,待方法直接返回即可,如果要终止重发,需要抛出error。
给Request指定RetryPolicy
Volley为每个请求设计成可以使用不同的重试策略,这通过方法setRetryPolicy来传入RetryPolicy 的一个实例。
public Request<?> setRetryPolicy(RetryPolicy retryPolicy)
如果没有调用这个方法,则默认使用DefaultRetryPolicy。
DefaultRetryPolicy
DefaultDetryPolicy可以是RetryPolicy接口的一个最简单的实现,它被包含了Volley库中,用作缺省的重发机制。
public class DefaultRetryPolicy implements RetryPolicy {
private int mCurrentTimeoutMs; // timeout值
private int mCurrentRetryCount; // 重试计数器
private final int mMaxNumRetries; // 最大重试次数
public static final int DEFAULT_TIMEOUT_MS = 2500;
public static final int DEFAULT_MAX_RETRIES = 1;
public static final float DEFAULT_BACKOFF_MULT = 1f;
public DefaultRetryPolicy() {
this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT);
}
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;
}
public float getBackoffMultiplier() {
return mBackoffMultiplier;
}
@Override
public void retry(VolleyError error) throws VolleyError {
mCurrentRetryCount++;
mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
if (!hasAttemptRemaining()) {
throw error;
}
}
protected boolean hasAttemptRemaining() {
return mCurrentRetryCount <= mMaxNumRetries;
}
}
通过retry方法可以看到,DefaultDetryPolicy的处理策略是,每次被触发时,将 计数器mCurrentRetryCount加1,当计算器mCurrentRetryCount 未超过最大重试次数mMaxNumRetries时,返回,这时请求将被重发。
注意这时超时时间mCurrentTimeoutMs增加一个比例mBackoffMultiplier,这表示重试时,会允许更久的超时时间,减少Timeout问题发生的几率。
如超过重试次数,则抛出error,停止重发。
对鉴权失败执行重发
如果使用OAuth的HTTP API,那么常常会遇到Access Token失效的情况,这时就要求我们使用Refresh Token重新换取Token,再重发当前请求。 我的做法如下:
@Override
public void retry(VolleyError error) throws VolleyError {
mCurrentRetryCount++;
mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
if (!hasAttemptRemaining()) {
throw error;
}
if (error instanceof AuthFailureError ) {
Token token = dan.getToken();
String refreshToken = token.getRefreshToken();
if( refreshToken !=null && !refreshToken.isEmpty() ) {
mAuth.refresh(new OnResultListener() {
@Override
public void onResult(Result result) {
if( result.isOK() )
mVolleyClient.addToRequestQueue( mRequest );
}
});
}
throw error;
}
}
在这里,使用error instanceof AuthFailureError 判断是否鉴权失败,如果是,则终止当前请求的重发,通过mAuth.refresh发送获取AccessToken请求,并在其正确的回应中将当前请求在加入队列。