笔者学习retrofit已经有一段时间了,虽然retrofit的轻易上手,使用简单,但是有一个问题一直是笔者心中的困惑,就是如何优雅地对某个接口的请求单独地执行某些行为,例如修改超时时间,是否对参数加密等,因为诸如超时时间这些设置是在初始化httpClient的时候就已经设置好的,并且是全局且唯一的,为了解决这个问题笔者花了大量的时间搜索互联网寻求解决方案,得到的有以下几种:
1.每次请求都生成新的retrofit。这种是极其恶劣的行为,因为这样会导致大量的内存浪费,这种方案一开始就被笔者抛弃了。
2.给请求设置特定含义的header。使用okhttp的拦截器捕获这些特定含义的header,然后执行操作(添加特定含义header案例)。笔者尝试过使用这种方案去构建过框架,但是随着需要定制的配置越来越多,代码显得越来越臃肿,而且也不优雅,也被笔者抛弃了。
3.修改retrofit源码,识别更多自定义的注解。(修改源码的案例)retrofit在网络框架中核心的工作就是根据注解和动态代理的设计思想生成对应的okhttp的request。因此,如果我们可以通过修改retrofit中的源码,让retrofit识别我们的注解,进而改变最终生成的request,即可解决这个问题。但是,这就意味着,我们要彻底和github上开源的retrofit脱轨,这种侵入性极大的方案也被笔者抛弃了。
那么,有没有一种非侵入性的,优雅的,简单的方案,可以达到配置单次请求的方案呢,是有的。
实际上,在retrofit2.6之后,作者就在源码中,就对request的tag中加入了对接口注解的设置,如果我们在okhttp自定义拦截器中获取tag,就可以获得该接口的所有注解信息了。
使用案例:
@Target(METHOD)
@Retention(RUNTIME)
public @interface ConnectionTime {
long connectionTime();
}
这里笔者创建了一个自定义注解,作用是定义某个接口的超时时间(覆盖全局配置),然后在retrofit的某个接口中使用该注解:
public interface TestApi {
@POST
@Headers({ "Content-Type: application/json;charset=UTF-8"})
@ConnectionTime(connectionTime = 5000)
Observable<String> getResult(@Url String url, @QueryMap Map<String, String> params);
}
这样该注解还没有发挥作用,我们自定义okhttp拦截器,在request的tag中获取该注解的值。
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
LogUtil.d("超时时间"+request.tag(Invocation.class).method().getAnnotation(ConnectionTime.class).connectionTime());
这样,我们就可以在自定义拦截器中截取到刚才定义的超时时间值了,然后重写request中的对应值,就可以达到只对该接口进行配置的目的。
注:这里笔者只是做个简单演示,实际上接口不一定会有这个注解,记得判空否则会导致空指针异常。