Volley請求的重發機制
暮鼓集 行走集
原作於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請求,並在其正確的迴應中將當前請求在加入佇列。