剖析Volley請求屢次的原理

網絡線程NetWorkDispather 的run 方法裏有一行代碼:NetworkResponse networkResponse = mNetwork.performRequest(request);意思開始請求服務端,直到返回響應,進performRequest方法看看:java

  public NetworkResponse performRequest(Request<?> request) throws VolleyError {
        long requestStart = SystemClock.elapsedRealtime();
        while (true) {
            HttpResponse httpResponse = null;
            byte[] responseContents = null;
            Map<String, String> responseHeaders = new HashMap<String, String>();
            try {
                // Gather headers.
                Map<String, String> headers = new HashMap<String, String>();
                addCacheHeaders(headers, request.getCacheEntry());
1                httpResponse = mHttpStack.performRequest(request, headers);
                StatusLine statusLine = httpResponse.getStatusLine();
                int statusCode = statusLine.getStatusCode();

                responseHeaders = convertHeaders(httpResponse.getAllHeaders());
                // Handle cache validation.
2                if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
                            request.getCacheEntry() == null ? null : request.getCacheEntry().data,
                            responseHeaders, true);
                }

                // Some responses such as 204s do not have content.  We must check.
                if (httpResponse.getEntity() != null) {
                  responseContents = entityToBytes(httpResponse.getEntity());
                } else {
                  // Add 0 byte response as a way of honestly representing a
                  // no-content request.
                  responseContents = new byte[0];
                }

                // if the request is slow, log it.
                long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
                logSlowRequests(requestLifetime, request, responseContents, statusLine);

                if (statusCode < 200 || statusCode > 299) {
                    throw new IOException();
                }
3                return new NetworkResponse(statusCode, responseContents, responseHeaders, false);
            } catch (SocketTimeoutException e) {
4                attemptRetryOnException("socket", request, new TimeoutError());
            } catch (ConnectTimeoutException e) {
5                attemptRetryOnException("connection", request, new TimeoutError());
            } catch (MalformedURLException e) {
6                throw new RuntimeException("Bad URL " + request.getUrl(), e);
            } 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());
7                if (responseContents != null) {
8                    networkResponse = new NetworkResponse(statusCode, responseContents,
                            responseHeaders, false);
                    if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
                            statusCode == HttpStatus.SC_FORBIDDEN) {
9                        attemptRetryOnException("auth",
                                request, new AuthFailureError(networkResponse));
                    } else {
                        // TODO: Only throw ServerError for 5xx status codes.
                        throw new ServerError(networkResponse);
                    }
                } else {
                    throw new NetworkError(networkResponse);
                }
            }
        }
    }

這個方法代碼有點長,但結構很清晰,循環執行這個請求,直到收到服務端響應,或者拋出異常。1處傳請求內容和請求頭,http通訊,直到返回一個HttpResponose對象,解析這個對象,2,3處返回結果。上述是正常的請求返回過程,若是這個過程發生了異常而且被捕獲,執行重發或者拋出異常。4,5處看到調用的方法的簽名可猜到嘗試從新請求,這是捕獲的異常都是超時異常。進到attempRetryException看看:網絡

 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));
            throw e;
        }
        request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
    }

這個方法的邏輯也簡單,只要不拋出異常,就正常執行,判斷是否拋異常就看1處,進retry方法看看
socket

  public void retry(VolleyError error) throws VolleyError {
        mCurrentRetryCount++;
        mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
1        if (!hasAttemptRemaining()) {
            throw error;
        }
    }

    /**
     * Returns true if this policy has attempts remaining, false otherwise.
     */
    protected boolean hasAttemptRemaining() {
2        return mCurrentRetryCount <= mMaxNumRetries;
    }

關鍵在hasAttempRemaining()方法,先mCurrentRetryCount++,而後和mMaxNumRetries比較,這裏的邏輯的意思是當前嘗試請求的次數大於最大請求數就拋出異常,這樣邏輯回到performRequest(),接着跳出循環,返回到網絡線程,有網絡線程處理拋出的異常。若是當前嘗試請求的次數小於最大請求數,不拋出異常,邏輯回到performRequest(),接着下一次循環,再次嘗試請求服務。在performRequest()的6處,說明請求url有問題,可能不合法,直接拋出RuntimeException異常返回網絡線程。在performRequest()的7處,說明此時拋出了IO異常,在responseContents不爲空的狀況下,再次構造響應實例networkResponse,若是是認證問題的話,再給從新請求的機會,邏輯就走到attempRetryException(),只不過要拋出的異常是authFailureException,除了這個狀況,其餘狀況都拋出異常返回網絡線程,處理異常。這樣咱們分析了請求屢次的原理,但又有疑問,在這個請求屢次怎麼設置它的次數呢,在剛纔對attempRetryException的分析中發現,判斷次數是否超出的邏輯是在一個叫DefaultRetryPolicy的類裏面實現的,在request能夠直接傳一個DefaultRetryPolicy的實例進去,具體的代碼是this


= JsonPostRequest(IMConfig.map).setRetryPolicy(DefaultRetryPolicy(DefaultRetryPolicy.* DefaultRetryPolicy.DefaultRetryPolicy.))

關注DefaultRetryPolicy的構造方法第二個參數,就是指定請求多少次的最大數。另外兩個參數是計算超時時間的。url

相關文章
相關標籤/搜索