Android中的volley_12_请求重试策略RetryPolicy和DefaultRetryPolicy

本文探讨了Android Volley库中的RetryPolicy接口及其默认实现DefaultRetryPolicy,详细解释了请求重试策略的工作原理,包括超时时间、重试次数的设置,并通过分析BasicNetwork如何处理请求重试,揭示了Volley中策略模式的应用。

面相接口的编程思想已经深入到volley的骨髓中了,当学习完volley,就算没有别的收货,但面相接口的编程思想必定深深印在脑海中,从HttpStack、到Network、再到Request等都是利用的接口思想。今天所说的请求重试策略RetryPolicy依然遵循了此思想,不得不令人感叹!!!

其实,直到现在这一刻,才忽然意识到,似乎volley就是利用接口来搭建其骨架的,之前的时候知道volley内利用了大量的接口,但是自己也只是明白,但是没有什么直观的概念,对于volley源码的学习和研究已经有段时间了,对其各个部分,也有了直观的了解,现在想想在各个关键点用的都是接口,骨架使用接口搭建的,而volley也有一套默认的对这些接口的实现类,形成了骨架间的血肉。嘿嘿,跑远了。

请求重试策略,很高大上的样子,但其内部结构真的很简单,先看看最直观的源码:

/**
 * Retry policy for a request.
 * 请求的重新请求策略
 */
public interface RetryPolicy {

    /**
     * Returns the current timeout (used for logging).
     * 获取当前请求用时(用于Log)
     */
    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;
}

接口就定义了三个方法:获取当前超时时间、获取重试次数和重试。前两个方法很直观,重要的是第三个方法public void retry(VolleyError error) throws VolleyError  ,而对于第三个方法想要强调的是throws VolleyError    抛出异常,也就是说在retry内部会抛出异常,之前一个字面retry就认为是在该方法内部重新发出网络请求,其实不是这样的,在内部的真正操作是变更重试策略的属性,如超时时间和重试次数,当超过了重试策略设定的限定就会抛出异常。注意注意注意:retry并不是真正的去重新发出网络请求。请求的重试是在BasicNetwork内实现的。稍后会涉及到。

DefaultRetryPolicy,RetryPolicy的默认实现类,也是volley的默认的请求重试策略。

该类内部定义了重试策略的必备属性:

/** The current timeout in milliseconds. 当前超时时间*/
    private int mCurrentTimeoutMs;

    /** The current retry count. 当前重试次数*/
    private int mCurrentRetryCount;

    /** The maximum number of attempts. 最大重试次数*/
    private final int mMaxNumRetries;

    /** The backoff multiplier for for the policy. 表示每次重试之前的 timeout 该乘以的因子,每重试一次,超时时间就变化一次*/
    private final float mBackoffMultiplier;

    /** The default socket timeout in milliseconds 默认超时时间*/
    public static final int DEFAULT_TIMEOUT_MS = 5000;

    /** The default number of retries  默认最大重试次数*/
    public static final int DEFAULT_MAX_RETRIES = 1;

DefaultRetryPolicy有两个构造方法,在创建实例时使用了默认的属性。

/**
     * Constructs a new retry policy using the default timeouts.
     * 构造时,使用了默认的几个参数
     */
    public DefaultRetryPolicy() {
        this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT);
    }

    /**
     * Constructs a new retry policy.
     * @param initialTimeoutMs The initial timeout for the policy.  当前超时时间
     * @param maxNumRetries The maximum number of retries.		最大重试次数
     * @param backoffMultiplier Backoff multiplier for the policy.
     */
    public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {
        mCurrentTimeoutMs = initialTimeoutMs;
        mMaxNumRetries = maxNumRetries;
        mBackoffMultiplier = backoffMultiplier;
    }

看另外两个方法:

/**
     * Prepares for the next retry by applying a backoff to the timeout.
     * @param error The error code of the last attempt.
     */
    @Override
    public void retry(VolleyError error) throws VolleyError {
    	//重试次数+1
        mCurrentRetryCount++;
        //超时时间每次都变化,增加
        mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
        if (!hasAttemptRemaining()) {
        	//如果超过了最大重试次数就抛出异常
            throw error;
        }
    }

    /**
     * Returns true if this policy has attempts remaining, false otherwise.
     * 当前重试次数是否已经达到最大重试次数
     */
    protected boolean hasAttemptRemaining() {
        return mCurrentRetryCount <= mMaxNumRetries;
    }

hasAttemptRemaining()方法返回的是,当前请求次数是否已经超过了最大的重试次数。retry()方法则是定义重试请求后属性的变化,如果超过了最大次数那么抛出异常。

到目前为止,好像除了规定了超时时间和重试次数,其他的跟请求重试也没什么关系。回顾下BasicNetwork内的方法:

/**
     * 执行具体的网络请求 ,需要Volley的Request,返回的是可以被传递的响应
     */
    @Override
    public NetworkResponse performRequest(Request<?> request) throws VolleyError {
        long requestStart = SystemClock.elapsedRealtime();
        //这里使用while(true)的含义是:保证请求重试策略的执行。
        //如果网络正常返回结果 那么直接return 
        //如果需要进行请求重试,就用到这里了,保证了可以进行请求重试
        while (true) {
            ......
            try {
               ...................
                return new NetworkResponse(statusCode, responseContents, responseHeaders, false);
            //如果发生超时,认证失败等错误,进行重试操作,直到成功、抛出异常(不满足重试策略等)结束
                //当catch后没有执行上边的return  而当前又是一个while(true)循环,可以保证下面的请求重试的执行,是利用循环进行请求重试,请求重试策略只是记录重试的次数、超时 时间等内容。
            } catch (SocketTimeoutException e) {
            	//当出现异常的时候,尝试进行请求重试
               <span style="color:#ffcc66;"> </span><span style="background-color: rgb(255, 255, 102);">attemptRetryOnException("socket", request, new TimeoutError());</span>
            } catch (ConnectTimeoutException e) {
            	//当出现异常的时候,尝试进行请求重试
               <span style="background-color: rgb(255, 255, 102);"> attemptRetryOnException("connection", request, new TimeoutError());</span>
            } catch (MalformedURLException e) {
            	//url不正常异常
                throw new RuntimeException("Bad URL " + request.getUrl(), e);
            } catch (IOException e) {
            	//当出现IO异常时,在try内读取数据体时,如果出现IO异常,那么捕获异常,继续完成创建NetworkResponse的过程
                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);
                    //如果状态码是授权未通过
                    if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
                            statusCode == HttpStatus.SC_FORBIDDEN) {
                    	//请求重试策略
                        <span style="background-color: rgb(255, 255, 102);">attemptRetryOnException("auth",
                                request, new AuthFailureError(networkResponse));</span>
                    } else {
                        // TODO: Only throw ServerError for 5xx status codes.
                        throw new ServerError(networkResponse);
                    }
                } else {
                    throw new NetworkError(networkResponse);
                }
            }
        }
    }

 public NetworkResponse performRequest(Request<?> request)方法是执行网络请求的方法,那么理所当然的要进行请求重试也是在这里进行。如何进行请求重试,注意在方法的内部是用while(true)括起来的,也就是说如果该方法正常执行完毕或者抛出异常时,必然就跳出循环了,但是如果请求失败没有return并且在catch内也没有超过重试策略限定条件时,必然会while(true)下重新请求一次,这样就达到了重试的目的。

看一下attemptRetryOnException()方法内部:

/**
     * 请求重试策略
     * Attempts to prepare the request for a retry. If there are no more attempts remaining in the
     * request's retry policy, a timeout exception is thrown.
     * @param request The request to use.
     */
    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));
            //这里才是最重要的,当仍然可以进行重试的时候,不会执行到catche语句,但是当执行到catch语句的时候,表示已经不能进行重试了,就抛出异常  这样while(true)循环就断了
            throw e;
        }
        //给请求添加标记,请求了多少次
        request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
    }

其实到这里为止,文章已经可以结束了,但是在写文章的过程中又有了新的领悟,关于策略的。

百度了下关于策略的定义:

策略,指计策;谋略。一般是指:1. 可以实现目标的方案集合;2. 根据形势发展而制定的行动方针和斗争方法;3. 有斗争艺术,能注意方式方法。

应用到程序开发中,应该是理解为:可以实现目标的方案集合。需要注意的是,策略本身并不会具体的去执行。

DefaultRetryPolicy是请求重试的策略,它规定了超时时间、超时时间的变化、请求重试次数、最大请求重试次数以及请求重试后的变化,但是DefaultRetryPolicy并没有去执行真正的网络重试请求,仅仅是规划了,真正的重试还是要到网络请求类中。

写到这里,想到了volley中的Request,也应该算是一种策略,Request中定义了跟网络请求相关的必备属性,如请求方式、超时时间、参数等,但是Request并不会去执行网络请求,真正执行网络请求的还是BasicNetwork。

策略,是方案。


demo下载地址:http://download.csdn.net/detail/vvzhouruifeng/8747599

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值