网络请求as

Volley之任务取消,重试机制(二)

2017-02-15  本文已影响194人  evil_ice

本篇文章分为两个部分

ok,现在就从第一问题开始

一,Volley的任务取消

1, 在网络请求的基类Request.java中

 /** Whether or not this request has been canceled. */
    private boolean mCanceled = false;
    
/** An opaque token tagging this request; used for bulk cancellation. */
    private Object mTag;
    ... ...
/**
     * Set a tag on this request. Can be used to cancel all requests with this
     * tag by {@link RequestQueue#cancelAll(Object)}.
     *
     * @return This Request object to allow for chaining.
     */
    public Request<?> setTag(Object tag) {
        mTag = tag;
        return this;
    }

    /**
     * Returns this request's tag.
     * @see Request#setTag(Object)
     */
    public Object getTag() {
        return mTag;
    }
   ...  ...

 /**
     * Mark this request as canceled.  No callback will be delivered.
     */
    public void cancel() {
        mCanceled = true;
    }

    /**
     * Returns true if this request has been canceled.
     */
    public boolean isCanceled() {
        return mCanceled;
    }

...  ...

  1. 单一取消请求
    在Request中有一个boolean类型变量mCanceled用来标识这个Request请求是否被取消. cancel()方法和 isCanceled()方法分别用来取消和获取是否取消
    所以当我们如果某个网络请求时,只需要request.cancel()即可.

不管是在NetworkDispatcher.java中,还是在CacheDispatcher.java还是返回结果的ExecutorDelivery.java中都有关于request请求是否取消的判断

NetworkDispatcher.java

public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        while (true) {
               ...  ...
                if (request.isCanceled()) {
                    request.finish("network-discard-cancelled");
                    continue;
                }
              ...  ...
    }

CacheDispatcher.java

public void run() {
     
        while (true) {
            try {
                // Get a request from the cache triage queue, blocking until
                // at least one is available.
                final Request<?> request = mCacheQueue.take();
                request.addMarker("cache-queue-take");

              ...  ... 
                if (request.isCanceled()) {
                    request.finish("cache-discard-canceled");
                    continue;
                }
            ...  ...
}

ExecutorDelivery.java

 public void run() {
            // If this request has canceled, finish it and don't deliver.
            if (mRequest.isCanceled()) {
                mRequest.finish("canceled-at-delivery");
                return;
            }
      ...  ...
}

由上可知,不管在网络请求的那个阶段(网络请求,缓存请求,结果打回等),都会对请求进行判断是否已经取消

接下来问题了, 上面说的取消单一某个请求直接request.cancel(),那么当我们需要批量取消request的时候怎么办呢?

  1. 批量取消请求
    在最上面贴出的Request.java代码中有private Object mTag变量,用来标识某一类的请求, 可以使用setTag(Object tag) 和getTag().当我们有批量取消请求的业务时,我们可以根据类别给不同的请求设置不同的tag,然后在RequestQueue.java中调用cancelAll()方法即可
public void cancelAll(RequestFilter filter) {
        synchronized (mCurrentRequests) {
            for (Request<?> request : mCurrentRequests) {
                if (filter.apply(request)) {
                    request.cancel();
                }
            }
        }
    }

    /**
     * Cancels all requests in this queue with the given tag. Tag must be non-null
     * and equality is by identity.
     */
    public void cancelAll(final Object tag) {
        if (tag == null) {
            throw new IllegalArgumentException("Cannot cancelAll with a null tag");
        }
        cancelAll(new RequestFilter() {
            @Override
            public boolean apply(Request<?> request) {
                return request.getTag() == tag;
            }
        });
    }

二,Volley的重试机制

如果未看Volley的代码,让我设计重试机制, 我会考虑在出错时,再次将这个请求添加到网络请求队列再次发起请求.然而,看了Volley的代码,他的设计并不是如此.下面让我们看下他是怎么设计的

与此重试机制相关的有两个重要的类:DefaultRetryPolicy.java和BasicNetwork.java

1,DefaultRetryPolicy.java
用来定义重试机制的策略,例如定义最大尝试次数,最大超时时间,超时时间的乘积因子等.
每次重试,变量mCurrentRetryCount就自增1,达到最大的尝试次数,将抛出异常,结束请求

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

protected boolean hasAttemptRemaining() {
        return mCurrentRetryCount <= mMaxNumRetries;
    }

2, BasicNetwork.java
下面然我们看下Volley是怎么实现这个重试机制的

 public NetworkResponse performRequest(Request<?> request) throws VolleyError {
        long requestStart = SystemClock.elapsedRealtime();
        while (true) {
              ... ...
                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 (MalformedURLException e) {
                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());
                if (responseContents != null) {
                    networkResponse = new NetworkResponse(statusCode, responseContents,
                            responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
                    if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
                            statusCode == HttpStatus.SC_FORBIDDEN) {
                        attemptRetryOnException("auth",
                                request, new AuthFailureError(networkResponse));
                    } else {
                        // TODO: Only throw ServerError for 5xx status codes.
                        throw new ServerError(networkResponse);
                    }
                } else {
                    throw new NetworkError(networkResponse);
                }
            }
        }
    }

在BasicNetwork.java中的performRequest(Request<?> request)中有一个while(true)死循环, 对的,重试机制就靠这个死循环

 private static void attemptRetryOnException(String logPrefix, Request<?> request,
            VolleyError exception) throws VolleyError {
        RetryPolicy retryPolicy = request.getRetryPolicy();
        int oldTimeout = request.getTimeoutMs();

        try {
            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));
    }

attemptRetryOnException()方法会会根据DefaultRetryPolicy对象来判断是否还需要重试,如果不需要,则直接throw error来退出死循环. 如果需要,那么不再抛出异常, 这样由于while(true)死循环就再次执行这个request请求. 避免了开头所说的再次将请求插入到网络访问队列等等繁琐的操作. 他这样设计,简洁,便于处理且减少无所谓的繁琐逻辑

上一篇下一篇

猜你喜欢

热点阅读