Volley超时重试机制
基础用法
Volley为开发者提供了可配置的超时重试机制,我们在使用时只需要为我们的Request设置自定义的RetryPolicy即可.
参考设置代码如下:
int DEFAULT_TIMEOUT_MS = 10000;
int DEFAULT_MAX_RETRIES = 3;
StringRequest stringRequest = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
@Override
public void onResponse(String s) {
LogUtil.i(TAG, "res=" + s);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
LogUtil.e(TAG, volleyError.toString());
}
});
// 设置Volley超时重试策略
stringRequest.setRetryPolicy(new DefaultRetryPolicy(
DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
RequestQueue requestQueue = VolleyManager.getInstance(context).getRequestQueue();
requestQueue.add(stringRequest);
基础知识——两种超时(请求超时和响应超时)
在讲解Volley的超时重试原理之前,需要先普及一下跟超时重试相关的异常.
org.apache.http.conn.ConnectTimeoutException
A timeout while connecting to an HTTP server or waiting for an available connection from an HttpConnectionManager.
连接HTTP服务端超时或者等待HttpConnectionManager返回可用连接超时,俗称请求超时.
java.net.SocketTimeoutException
Signals that a timeout has occurred on a socket read or accept.
Socket通信超时,即从服务端读取数据时超时,俗称响应超时.
Volley就是通过捕捉这两个异常来进行超时重试的.
Volley捕捉超时异常
看过之前Volley源码分析的同学,应该知道Volley中是通过BasicNetwork去执行网络请求的.相关源码如下:
@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
// 记录请求开始的时间,便于进行超时重试
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
Map<String, String> responseHeaders = Collections.emptyMap();
try {
// ......省略部分添加HTTP-HEADER的代码 // 调用HurlStack的performRequest方法执行网络请求, 并将请求结果存入httpResponse变量中
httpResponse = mHttpStack.performRequest(request, headers); StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode();
responseHeaders = convertHeaders(httpResponse.getAllHeaders()); // ......省略部分对返回值处理的代码
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 (IOException e) {
// 省略对IOException的异常处理,不考虑服务端错误的重试机制
}
}
}
private void attemptRetryOnException(String logPrefix, Request<?> request, VolleyError exception) throws VolleyError{
RetryPolicy retryPolicy = request.getRetryPolicy();
int oldTimeout = request.getTimeoutMs(); retryPolicy.retry(exception);
Log.e("Volley", String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
}
因为BasicNetwork类中是用java while(true)包裹连接请求,因此如果捕捉到程序抛出SocketTimeoutException或者ConnectTimeoutException,并不会跳出退出循环的操作,而是进入到attemptRetryOnException方法.
如果attemptRetryOnException方法中没有抛出VolleyError异常,最终程序还是可以再次进入while循环,从而完成超时重试机制.
接下来,我们看一下RetryPolicy是如何判断是否需要进行超时重试,并且是如何停止超时重试的.
RetryPolicy.java(抽象接口)
RetryPolicy是一个接口定义,中文注释的源码如下:
/**
* Request请求的重试策略类.
*/
@SuppressWarnings("unused")
public interface RetryPolicy {
/**
* 获取当前请求的超时时间
*/
int getCurrentTimeout(); /**
* 获取当前请求的重试次数
*/
int getCurrentRetryCount(); /**
* 实现类需要重点实现的方法,用于判断当前Request是否还需要再进行重试操作
*/
void retry(VolleyError error) throws VolleyError;
}
---------------------
RetryPolicy只是Volley定义的Request请求重试策略接口,同时也提供了DefaultRetryPolicy实现类来帮助开发者来快速实现自定制的请求重试功能.
DefaultRetryPolicy.java
中文注释的源码如下:
public class DefaultRetryPolicy implements RetryPolicy {
/**
* Request当前超时时间
*/
private int mCurrentTimeoutMs;
/**
* Request当前重试次数
*/
private int mCurrentRetryCount;
/**
* Request最多重试次数
*/
private final int mMaxNumRetries;
/**
* Request超时时间乘积因子
*/
private final float mBackoffMultiplier;
/**
* Volley默认的超时时间(2.5s)
*/
public static final int DEFAULT_TIMEOUT_MS = 2500;
/**
* Volley默认的重试次数(0次,不进行请求重试)
*/
public static final int DEFAULT_MAX_RETRIES = 0;
/**
* 默认超时时间的乘积因子.
* 以默认超时时间为2.5s为例:
* 1. DEFAULT_BACKOFF_MULT = 1f, 则每次HttpUrlConnection设置的超时时间都是2.5s*1f*mCurrentRetryCount.
* 2. DEFAULT_BACKOFF_MULT = 2f, 则第二次超时时间为:2.5s+2.5s*2=7.5s,第三次超时时间为:7.5s+7.5s*2=22.5s
*/
public static final float DEFAULT_BACKOFF_MULT = 1f;
/**
* Request的默认重试策略构造函数
* 超时时间:2500ms
* 重试次数:0次
* 超时时间因子:1f
*/
public DefaultRetryPolicy() {
this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT);
}
/**
* 开发者自定制Request重试策略构造函数
*
* @param initialTimeoutMs 超时时间
* @param maxNumRetries 最大重试次数
* @param backoffMultiplier 超时时间乘积因子
*/
public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {
mCurrentTimeoutMs = initialTimeoutMs;
mMaxNumRetries = maxNumRetries;
mBackoffMultiplier = backoffMultiplier;
}
@Override
public int getCurrentTimeout() {
return mCurrentTimeoutMs;
}
@Override
public int getCurrentRetryCount() {
return mCurrentRetryCount;
}
@Override
public void retry(VolleyError error) throws VolleyError {
// 添加重试次数
mCurrentRetryCount ++;
// 累加超时时间
mCurrentTimeoutMs += mCurrentTimeoutMs * mBackoffMultiplier;
// 判断是否还有剩余次数,如果没有,则抛出VolleyError异常
if (!hasAttemptRemaining()) {
throw error;
}
}
/**
* 判断当前Request的重试次数是否超过最大重试次数
*/
private boolean hasAttemptRemaining() {
return mCurrentTimeoutMs <= mMaxNumRetries;
}
}
---------------------
本文开始时就讲过Request如何设置自定制的RetryPolicy,结合中文注释的DefaultRetryPolicy源码,相信大家很容易就能理解自定制RetryPolicy的参数含义和作用.
目前可能大家还有一个疑问,为什么DefaultRetryPolicy的retry方法抛出VolleyError异常,就能退出BasicNetwork类performRequest的while(true)循环呢?
这是因为BasicNetwork并没有捕获VolleyError异常,因此没有被try&catch住的异常会终止当前程序的运行,继续往外抛出,这时候就回到NetworkDispatcher类,相关源码如下:
@Override
public void run() {
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
long startTimeMs = SystemClock.elapsedRealtime();
Request<?> request;
try {
// 使用BlockingQueue实现了生产者-消费者模型.
// 消费者是该调度线程.
// 生产者是request网络请求.
request = mQueue.take();
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
continue;
} try {
if (request.isCanceled()) {
continue;
} addTrafficStatsTag(request); // 真正执行网络请求的地方.(BasicNetwork由于超时抛出的VolleyError会抛出到这里)
NetworkResponse networkResponse = mNetwork.performRequest(request); mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
// 捕获VolleyError异常,通过主线程Handler回调用户设置的ErrorListener中的onErrorResponse回调方法.
volleyError.printStackTrace();
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
parseAndDeliverNetworkError(request, volleyError);
} catch (Exception e) {
VolleyError volleyError = new VolleyError(e);
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
mDelivery.postError(request, volleyError);
}
}
}
---------------------
从上述源码中可以看出,Request无法继续重试后抛出的VolleyError异常,会被NetworkDispatcher捕获,然后通过Delivery去回调用户设置的ErrorListener.
小结
至此,Volley的超时重试机制就分析完了,在本文末尾给大家推荐一下Volley默认重试策略的参数.
默认超时时间:Volley的默认2500ms确实有点短,大家可以设置成HttpClient的默认超时时间,也就是10000ms.
默认重试次数:建议为3次,可根据业务自行调整.
默认超时时间因子:建议采用DefaultRetryPolicy的默认值1f即可,不然曲线增长太快会造成页面长时间的等待.
更详细的Volley源码分析,大家可以参考我的GitHub项目:Volley源码分析
--------------------- --------------------
作者:低调小一
来源:CSDN
原文:https://blog.csdn.net/wzy_1988/article/details/53445958
版权声明:本文为博主原创文章,转载请附上博文链接!
Volley超时重试机制的更多相关文章
- Dubbo超时重试机制带来的数据重复问题
Dubbo的超时重试机制为服务容错.服务稳定提供了比较好的框架支持,但是在一些比较特殊的网络环境下(网络传输慢,并发多)可能 由于服务响应慢,Dubbo自身的超时重试机制(服务端的处理时间超过了设定的 ...
- springcloud超时重试机制的先后顺序
https://blog.csdn.net/zzzgd_666/article/details/83314833
- dubbo超时重试和异常处理
dubbo超时重试和异常处理 dubbo超时重试和异常处理 参考: https://www.cnblogs.com/ASPNET2008/p/7292472.html https://www.tuic ...
- dobbo 服务配置详解(解决超时重试问题)
<!-- reference method --> <dubbo:reference interface="com.xx.XxxService"> ...
- nginx的重试机制以及nginx常用的超时配置说明
nginx的重试机制 现在对外服务的网站,很少只使用一个服务节点,而是部署多台服务器,上层通过一定机制保证容错和负载均衡. nginx就是常用的一种HTTP和反向代理服务器,支持容错和负载均衡. ng ...
- springcloud之Feign、ribbon设置超时时间和重试机制的总结
一 超时时间配置 如果在一个微服务当中对同一个接口同时配置了Hystrix与ribbon两个超时时间,则在接口调用的时候,两个计时器会同时读秒. 比如,访问一个接口需要2秒,你的ribbon配置的超时 ...
- 【Dubbo 源码解析】07_Dubbo 重试机制
Dubbo 重试机制 通过前面 Dubbo 服务发现&引用 的分析,我们知道,Dubbo 的重试机制是通过 com.alibaba.dubbo.rpc.cluster.support.Fail ...
- SpringCloud | FeignClient和Ribbon重试机制区别与联系
在spring cloud体系项目中,引入的重试机制保证了高可用的同时,也会带来一些其它的问题,如幂等操作或一些没必要的重试. 今天就来分别分析一下 FeignClient 和 Ribbon 重试机制 ...
- Ribbon重试机制与Hystrix熔断机制的配置问题1
Ribbon超时与Hystrix超时问题,为了确保Ribbon重试的时候不被熔断,我们就需要让Hystrix的超时时间大于Ribbon的超时时间,否则Hystrix命令超时后,该命令直接熔断,重试机制 ...
随机推荐
- vue.js 中双向绑定的实现---初级
1. 1 我们看到的变量,其实都不是独立的,它们都是windows对象上的属性 <!DOCTYPE html> <html lang="en"> <h ...
- 尚硅谷springboot学习14-自动配置原理
配置文件能配置哪些属性 配置文件能配置的属性参照 自动配置的原理 1).SpringBoot启动的时候加载主配置类,开启了自动配置功能 @EnableAutoConfiguration 2).@Ena ...
- Lazarus中Windows单元问题
在程序中加入Windows 单元后,经常会使一些过程和函数莫名其妙的报错,这是因为Windows单元很多函数,过程 与sysutils 单元重名 ,所以一般要把windows 引用放在 sysutil ...
- C#开发VS LUA开发
一个游戏公司,决定开始用U3D做一款新游戏,这个游戏类型从来没做过. 如果没有一个成熟的游戏框架,那么从头撸起. 是一开始就将LUA热更新考虑进来呢 还是先做成纯C#的框架呢? 考虑因素:游戏逻辑如果 ...
- mysql decimal
可能做程序的人都知道,float类型是可以存浮点数(即小数类型),但是float有个坏处,当你给定的数据是整数的时候,那么它就以整数给你处理. 这样我们在存取货币值的时候自然遇到问题,我的defaul ...
- C++ new 和malloc 区别
1.分配地方不同,malloc是堆上面,new是自由存储区域 2.malloc/delete是函数,new/delete是操作符,可以重载 3.malloc 要指定大小,返回的是void*指针,开辟的 ...
- 吴裕雄 09-MySQL删除数据表
以下为删除MySQL数据表的通用语法:DROP TABLE table_name; DROP TABLE runoob_tbl; 使用PHP脚本删除数据表PHP使用 mysqli_query 函数来删 ...
- BOS物流项目第十三天
教学计划 1.Quartz概述 a. Quartz介绍和下载 b. 入门案例 c. Quartz执行流程 d. cron表达式 2.在BOS项目中使用Quartz创建定时任务 3.在BOS项目中使用J ...
- 手机调试 fiddler
使用Fiddler调试手机程序 | 前端和运维利器 2015-11-27 zhy97031 文章来源 阅 688 转 4 转藏到我的图书馆 微信分享: 作者:Uncle Chen 原文 ...
- 微信小程序--分享报错(thirdScriptError Cannot read property 'from' of undefined;at pages/index/index page onShareAppMessage function TypeError: Cannot read property 'from' of undefined)
分享功能: onShareAppMessage: function (res) { if (res.from === 'button') { // 来自页面内转发按钮 console.log(res. ...