剖析Volley请求多次的原理

网络线程NetWorkDispather 的run 方法里有一行代码:NetworkResponse networkResponse = mNetwork.performRequest(request);意思开始请求服务端,直到返回响应,进performRequest方法看看:

  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方法看看

  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的实例进去,具体的代码是


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

关注DefaultRetryPolicy的构造方法第二个参数,就是指定请求多少次的最大数。另外两个参数是计算超时时间的。

你可能感兴趣的:(剖析Volley请求多次的原理)