Volley超時重試機制
基礎用法
Volley為開發者提供了可配置的超時重試機制,我們在使用時只需要為我們的Request設定自定義的RetryPolicy即可.
參考設定程式碼如下:
int DEFAULT_TIMEOUT_MS = 10000; int DEFAULT_MAX_RETRIES = 3; StringRequest stringRequest = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() { @Override public void onResponse(String s) { LogUtil.i(TAG,"res=" + s); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError volleyError) { LogUtil.e(TAG, volleyError.toString()); } }); // 設定Volley超時重試策略 stringRequest.setRetryPolicy(new DefaultRetryPolicy( DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT)); RequestQueue requestQueue= VolleyManager.getInstance(context).getRequestQueue(); requestQueue.add(stringRequest);
基礎知識——兩種超時(請求超時和響應超時)
在講解Volley的超時重試原理之前,需要先普及一下跟超時重試相關的異常.
org.apache.http.conn.ConnectTimeoutException
A timeout while connecting to an HTTP server or waiting for an available connection from an HttpConnectionManager.
連線HTTP服務端超時或者等待HttpConnectionManager返回可用連線超時,俗稱請求超時.
java.net.SocketTimeoutException
Signals that a timeout has occurred on a socket read or accept.
Socket通訊超時,即從服務端讀取資料時超時,俗稱響應超時.
Volley就是通過捕捉這兩個異常來進行超時重試的.
Volley捕捉超時異常
看過之前Volley原始碼分析的同學,應該知道Volley中是通過BasicNetwork去執行網路請求的.相關原始碼如下:
@Override 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 { // ......省略部分新增HTTP-HEADER的程式碼 // 呼叫HurlStack的performRequest方法執行網路請求, 並將請求結果存入httpResponse變數中 httpResponse = mHttpStack.performRequest(request, headers); StatusLine statusLine = httpResponse.getStatusLine(); int statusCode = statusLine.getStatusCode(); responseHeaders = convertHeaders(httpResponse.getAllHeaders()); // ......省略部分對返回值處理的程式碼 return new NetworkResponse(statusCode, responseContents, responseHeaders, false, SystemClock.elapsedRealtime() - requestStart); } catch (SocketTimeoutException e) { // 捕捉響應超時 attemptRetryOnException("socket", request, new TimeoutError()); } catch (ConnectTimeoutException E) { // 捕捉請求超時 attemptRetryOnException("connection", request, new TimeoutError()); } catch (IOException e) { // 省略對IOException的異常處理,不考慮服務端錯誤的重試機制 } } } private void attemptRetryOnException(String logPrefix, Request<?> request, VolleyError exception) throws VolleyError{ RetryPolicy retryPolicy = request.getRetryPolicy(); int oldTimeout = request.getTimeoutMs(); retryPolicy.retry(exception); Log.e("Volley", String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout)); }
因為BasicNetwork類中是用java while(true)包裹連線請求,因此如果捕捉到程式丟擲SocketTimeoutException或者ConnectTimeoutException,並不會跳出退出迴圈的操作,而是進入到attemptRetryOnException方法.
如果attemptRetryOnException方法中沒有丟擲VolleyError異常,最終程式還是可以再次進入while迴圈,從而完成超時重試機制.
接下來,我們看一下RetryPolicy是如何判斷是否需要進行超時重試,並且是如何停止超時重試的.
RetryPolicy.java(抽象介面)
RetryPolicy是一個介面定義,中文註釋的原始碼如下:
/** * Request請求的重試策略類. */ @SuppressWarnings("unused") public interface RetryPolicy { /** * 獲取當前請求的超時時間 */ int getCurrentTimeout(); /** * 獲取當前請求的重試次數 */ int getCurrentRetryCount(); /** * 實現類需要重點實現的方法,用於判斷當前Request是否還需要再進行重試操作 */ void retry(VolleyError error) throws VolleyError; } ---------------------
RetryPolicy只是Volley定義的Request請求重試策略介面,同時也提供了DefaultRetryPolicy實現類來幫助開發者來快速實現自定製的請求重試功能.
DefaultRetryPolicy.java
中文註釋的原始碼如下:
public class DefaultRetryPolicy implements RetryPolicy { /** * Request當前超時時間 */ private int mCurrentTimeoutMs; /** * Request當前重試次數 */ private int mCurrentRetryCount; /** * Request最多重試次數 */ private final int mMaxNumRetries; /** * Request超時時間乘積因子 */ private final float mBackoffMultiplier; /** * Volley預設的超時時間(2.5s) */ public static final int DEFAULT_TIMEOUT_MS = 2500; /** * Volley預設的重試次數(0次,不進行請求重試) */ public static final int DEFAULT_MAX_RETRIES = 0; /** * 預設超時時間的乘積因子. * 以預設超時時間為2.5s為例: * 1. DEFAULT_BACKOFF_MULT = 1f, 則每次HttpUrlConnection設定的超時時間都是2.5s*1f*mCurrentRetryCount. * 2. DEFAULT_BACKOFF_MULT = 2f, 則第二次超時時間為:2.5s+2.5s*2=7.5s,第三次超時時間為:7.5s+7.5s*2=22.5s */ public static final float DEFAULT_BACKOFF_MULT = 1f; /** * Request的預設重試策略建構函式 * 超時時間:2500ms * 重試次數:0次 * 超時時間因子:1f */ public DefaultRetryPolicy() { this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT); } /** * 開發者自定製Request重試策略建構函式 * * @param initialTimeoutMs 超時時間 * @param maxNumRetries 最大重試次數 * @param backoffMultiplier 超時時間乘積因子 */ 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; // 判斷是否還有剩餘次數,如果沒有,則丟擲VolleyError異常 if (!hasAttemptRemaining()) { throw error; } } /** * 判斷當前Request的重試次數是否超過最大重試次數 */ private boolean hasAttemptRemaining() { return mCurrentTimeoutMs <= mMaxNumRetries; } } ---------------------
本文開始時就講過Request如何設定自定製的RetryPolicy,結合中文註釋的DefaultRetryPolicy原始碼,相信大家很容易就能理解自定製RetryPolicy的引數含義和作用.
目前可能大家還有一個疑問,為什麼DefaultRetryPolicy的retry方法丟擲VolleyError異常,就能退出BasicNetwork類performRequest的while(true)迴圈呢?
這是因為BasicNetwork並沒有捕獲VolleyError異常,因此沒有被try&catch住的異常會終止當前程式的執行,繼續往外丟擲,這時候就回到NetworkDispatcher類,相關原始碼如下:
@Override public void run() { android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); while (true) { long startTimeMs = SystemClock.elapsedRealtime(); Request<?> request; try { // 使用BlockingQueue實現了生產者-消費者模型. // 消費者是該排程執行緒. // 生產者是request網路請求. request = mQueue.take(); } catch (InterruptedException e) { // We may have been interrupted because it was time to quit. if (mQuit) { return; } continue; } try { if (request.isCanceled()) { continue; } addTrafficStatsTag(request); // 真正執行網路請求的地方.(BasicNetwork由於超時丟擲的VolleyError會丟擲到這裡) NetworkResponse networkResponse = mNetwork.performRequest(request); mDelivery.postResponse(request, response); } catch (VolleyError volleyError) { // 捕獲VolleyError異常,通過主執行緒Handler回撥使用者設定的ErrorListener中的onErrorResponse回撥方法. volleyError.printStackTrace(); volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); parseAndDeliverNetworkError(request, volleyError); } catch (Exception e) { VolleyError volleyError = new VolleyError(e); volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); mDelivery.postError(request, volleyError); } } } ---------------------
從上述原始碼中可以看出,Request無法繼續重試後丟擲的VolleyError異常,會被NetworkDispatcher捕獲,然後通過Delivery去回撥使用者設定的ErrorListener.
小結
至此,Volley的超時重試機制就分析完了,在本文末尾給大家推薦一下Volley預設重試策略的引數.
預設超時時間:Volley的預設2500ms確實有點短,大家可以設定成HttpClient的預設超時時間,也就是10000ms.
預設重試次數:建議為3次,可根據業務自行調整.
預設超時時間因子:建議採用DefaultRetryPolicy的預設值1f即可,不然曲線增長太快會造成頁面長時間的等待.
更詳細的Volley原始碼分析,大家可以參考我的GitHub專案:Volley原始碼分析
--------------------- --------------------
作者:低調小一
來源:CSDN
原文:https://blog.csdn.net/wzy_1988/article/details/53445958
版權宣告:本文為博主原創文章,轉載請附上博文連結!