Android中的volley_12_请求重试策略RetryPolicy和DefaultRetryPolicy
2015-06-06 08:31
501 查看
面相接口的编程思想已经深入到volley的骨髓中了,当学习完volley,就算没有别的收货,但面相接口的编程思想必定深深印在脑海中,从HttpStack、到Network、再到Request等都是利用的接口思想。今天所说的请求重试策略RetryPolicy依然遵循了此思想,不得不令人感叹!!!
其实,直到现在这一刻,才忽然意识到,似乎volley就是利用接口来搭建其骨架的,之前的时候知道volley内利用了大量的接口,但是自己也只是明白,但是没有什么直观的概念,对于volley源码的学习和研究已经有段时间了,对其各个部分,也有了直观的了解,现在想想在各个关键点用的都是接口,骨架使用接口搭建的,而volley也有一套默认的对这些接口的实现类,形成了骨架间的血肉。嘿嘿,跑远了。
请求重试策略,很高大上的样子,但其内部结构真的很简单,先看看最直观的源码:
接口就定义了三个方法:获取当前超时时间、获取重试次数和重试。前两个方法很直观,重要的是第三个方法public void retry(VolleyError error) throws VolleyError ,而对于第三个方法想要强调的是throws VolleyError 抛出异常,也就是说在retry内部会抛出异常,之前一个字面retry就认为是在该方法内部重新发出网络请求,其实不是这样的,在内部的真正操作是变更重试策略的属性,如超时时间和重试次数,当超过了重试策略设定的限定就会抛出异常。注意注意注意:retry并不是真正的去重新发出网络请求。请求的重试是在BasicNetwork内实现的。稍后会涉及到。
DefaultRetryPolicy,RetryPolicy的默认实现类,也是volley的默认的请求重试策略。
该类内部定义了重试策略的必备属性:
DefaultRetryPolicy有两个构造方法,在创建实例时使用了默认的属性。
再看另外两个方法:
hasAttemptRemaining()方法返回的是,当前请求次数是否已经超过了最大的重试次数。retry()方法则是定义重试请求后属性的变化,如果超过了最大次数那么抛出异常。
到目前为止,好像除了规定了超时时间和重试次数,其他的跟请求重试也没什么关系。回顾下BasicNetwork内的方法:
public NetworkResponse performRequest(Request<?> request)方法是执行网络请求的方法,那么理所当然的要进行请求重试也是在这里进行。如何进行请求重试,注意在方法的内部是用while(true)括起来的,也就是说如果该方法正常执行完毕或者抛出异常时,必然就跳出循环了,但是如果请求失败没有return并且在catch内也没有超过重试策略限定条件时,必然会while(true)下重新请求一次,这样就达到了重试的目的。
看一下attemptRetryOnException()方法内部:
其实到这里为止,文章已经可以结束了,但是在写文章的过程中又有了新的领悟,关于策略的。
百度了下关于策略的定义:
策略,指计策;谋略。一般是指:1. 可以实现目标的方案集合;2. 根据形势发展而制定的行动方针和斗争方法;3. 有斗争艺术,能注意方式方法。
应用到程序开发中,应该是理解为:可以实现目标的方案集合。需要注意的是,策略本身并不会具体的去执行。
DefaultRetryPolicy是请求重试的策略,它规定了超时时间、超时时间的变化、请求重试次数、最大请求重试次数以及请求重试后的变化,但是DefaultRetryPolicy并没有去执行真正的网络重试请求,仅仅是规划了,真正的重试还是要到网络请求类中。
写到这里,想到了volley中的Request,也应该算是一种策略,Request中定义了跟网络请求相关的必备属性,如请求方式、超时时间、参数等,但是Request并不会去执行网络请求,真正执行网络请求的还是BasicNetwork。
策略,是方案。
demo下载地址:http://download.csdn.net/detail/vvzhouruifeng/8747599
其实,直到现在这一刻,才忽然意识到,似乎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
相关文章推荐
- Android编程读写首选项
- Android——SharedPreferences实现登录界面的记住密码和自动登录功能
- 菜鸟学Android笔记(二十四):ServletContext1
- 在android工程中导入图片
- 无法打开Android SDK Manager的解决办法
- 菜鸟学Android笔记(二十三):ServletConfig
- android style 退出动画 解决退出动画无效问题
- android窗体动画:activity启动从底部向上滑动出现,关闭的时候从顶部向下滑动消失的动画实现
- 6.5android 自定义相机,以及部分手机系统相机的bug
- Android自定义控件
- Android实现QQ第三方登录
- Android应用之最新版本SDKV2.4实现QQ第三方登录
- Qt for android 截屏、Qml保存图片到本地
- Android学习日记(10)
- Android抽象布局——include、merge 、ViewStub .
- Android学习日记(9)
- Spinner的OnItemSelectedListener事件(在布局文件中通过Spinner的android:entries属性加载列表)
- Android中Menu的基本用法 .
- Android之drawable state各个属性详解 .
- 【Android】ListView 优化