最近重构项目需要调整,
需要区分普通上传和辅助功能校验的超时.
为了提高用户体验,需要动态去进行配置.
最传统的设置 请求超时时间的方法无疑是
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectTimeout(connTimeout, TimeUnit.SECONDS)
.readTimeout(connTimeout, TimeUnit.SECONDS)
.writeTimeout(connTimeout, TimeUnit.SECONDS));
在创建OkhttpClient的时候,传入指定的timeout,
还可以在这里加上各种自定义拦截器(比如日志输出,user_agent,加签)
这个当然可以.
但是如果是一个大型项目,一个host的service内有着大量的请求接口.
在请求的时候,无疑是需要对OkHttpClient进行单例复用的,
这个时候需要对这种同一个host的其中的几个请求接口的超时时间进行独立控制,就不好处理了.
当然还有一种办法,
在创建OkHttpClient的时候,新增一个自定义拦截器,继承Interceptor,然后维护一个需要独立处理请求超时的地址集合.
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
String questUrl = request.url().toString();
//在这里进行请求地址判断.
if ( isContains(questUrl)){
Response proceed = chain.withConnectTimeout(timeout, TimeUnit.SECONDS)
.withReadTimeout(timeout, TimeUnit.SECONDS)
.withWriteTimeout(timeout, TimeUnit.SECONDS)
.proceed(request);
return proceed;
}
return chain.proceed(request);
}
通过维护一个地址集合,然后判断地址,再设置对应的超时时间.
这种方法也可以,
但是如果某些地址需要超时1秒,某些需要超时2秒,某些需要超时6秒,
那么可以预期得到,这样写起来会很蛋疼,
那么有没有什么更好的办法?
当然是有的!
经过上面的铺垫,
前面的2种方法,多多少少都有着一点缺陷和遗憾.
那么现在隆重介绍第3种方法,也是我个人认为最佳也是最优雅的办法.
首先请将Retrofit升级至 2.5.0
首先请将Retrofit升级至 2.5.0
首先请将Retrofit升级至 2.5.0
重要的事情说3遍.
先创建一个自定义注解,
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DynamicTimeout {
int timeout();
}
注解名称可以按照自己的习惯起,代码内容也通俗易懂,当然也可以再加一个参数,比如说 Unit,也就是时间单位,指定设置超时时间是 毫秒或者 秒,这看具体的个人需求.
使用起来也很简单,直接在指定方法上面添加注解,设置指定的超时时间即可,
简单易用!
还是创建一个拦截器
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
//核心代码!!!
final Invocation tag = request.tag(Invocation.class);
final Method method = tag != null ? tag.method() : null;
final DynamicTimeout timeout = method != null ? method.getAnnotation(DynamicTimeout.class) : null;
XLog.d("invocation",tag!= null ? tag.toString() : "");
if(timeout !=null && timeout.timeout() > 0){
Response proceed = chain.withConnectTimeout(timeout.timeout(), TimeUnit.SECONDS)
.withReadTimeout(timeout.timeout(), TimeUnit.SECONDS)
.withWriteTimeout(timeout.timeout(), TimeUnit.SECONDS)
.proceed(request);
return proceed;
}
return chain.proceed(request);
}
注意看注释,标注的那3行核心代码.
在Request内,通过tag,获取到Invocation,
然后再获取Invocaion对象的method,最后获取method的注解信息,
判断注解对象存不存在,
如果存在直接读取对应注解对象的设置参数值,动态设置进去.
Wow~
Awesome !!!
作为一个有追求的程序员,当然会很好奇,
这玩意是怎么实现的 以及 举一反三.
能不能用这个再去做一点其他骚操作,来改善一下我们现有的代码呢?
Invocation这个类点进去一看,
平平无奇…
还是去看看他的method属性是怎么设置进去的吧.
我们在RequestFactory内看到了是如何设置tag,
以及设置进去的tag也就是Invocation对象内如何保存 method和args的.
那么这2个属性的来源呢
当然是来自于Retrofit的核心,这个动态代理方法.
具体的源码分析也不讲了,
网上实在是太多了,我自己在2018年底也写了一篇.
看了一圈,Invocation这个对象是有2个属性的,我们只用到了method,
另外一个args呢,
我们其实也可以拿来利用一下.
比如 一些定义要在header内添加的 sign,user-agent,
可以通过invocation.arguments()获取到,然后进行处理.
这些拓展用法就不细说了.
OK,我们回顾完所有的代码,最后的总结就是