V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
BakerJ
V2EX  ›  Android

Android 开发 - RxRetroHttp,为多套 API 请求适配而生

  •  
  •   BakerJ · 2019-01-28 16:34:29 +08:00 · 8506 次点击
    这是一个创建于 2125 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前言

    "后端更新换代,新接口返回全用新的规则,老接口不变!"。。。WTF !

    “我们的这几个网站,要做一个统一的 App,后端都是现成的,这是 API 文档。”。。。几个网站的 API 规范和请求 Host 地址居然完全不一样?。。。WTF !

    。。。千万只草泥马呼啸而过。。。实时切换 BaseUrl ? Retrofit 注解全加上 @Url ?。。。无奈。。。

    虽然说现在已经有很多 Http 请求框架了,也有很多针对 RxJava+Retrofit 的二次封装,其中也不乏很多动态替换 BaseUrl 的框架。但是如果需要更好的处理除了 BaseUrl 之外需求,比如针对各套 API 规则,不同的拦截处理、不同的返回异常逻辑处理等等,大多没有给予解决方案。因此,RxRetroHttp 应运而生。

    Github 地址: https://github.com/BakerJQ/RxRetroHttp

    总览

    我们先来看看,RxRetroHttp 是通过什么方式处理这种情况的。

    初始化

    首先,大多库的必备阶段:初始化。我们先来看看初始化的代码,在 Application 的 onCreate 中执行

    RxRetroHttp.init(this)
               .setBaseUrl("http://api1.com/")
               .setApiResultClass(Api1Result.class)
               .generateRetroClient()
    

    这样,初始化就做完了。。。此处应有掌声。。。

    “我掌你大爷!!!说好的处理多套 API 规则呢!!!”

    额咳。。。客观莫急。。。待我徐徐道来

    通过刚刚的初始化,你已经设置了 App 中主 API 请求的基本配置。如果你的 App 中,就像前言里描述的那样,需要对接多套 API 规则,那么在初始化之后,再加入如下代码

    RxRetroHttp.getInstance()
               .setBaseUrl("https://api2.com/")
               .setApiResultClass(Api2Result.class)
               .generateRetroClient("API2")
    

    相信大家已经看出区别了吧,没错,就是在 generateRetroClient 这个方法中,加入了一个 Tag,而这个 Tag,就是处理多套 API 请求的关键。

    在 setApiResultClass 方法中,传入的就是对于 API 规范的基类,具体情况会在后面讲到。

    调用

    初始化完成后,如何调用呢

    RxRetroHttp.create(Api2Service.class).getApi2Info()
    

    我们可以看到,这就是 Retrofit 风格的调用方式。

    在这里,Api2Service 也就是 Retrofit 风格的 ApiService,但是也略有不同

    @RetroTag("API2")
    public interface Api2Service {
        @GET("test/info")
        Observable<Api2Info> getApi2Info();
    }
    

    我们看看不同在哪,下面是纯 Retrofit 的书写方式

    public interface Api2Service {
        @GET("test/info")
        Observable<Api2Result<Api2Info>> getApi2Info();
    }
    

    没错,区别就在于:

    1、省去了基类的这一层包裹。这么做的原因是,个人认为,在 ApiService 这一层,每个接口定义都需要设置 ApiResult 包裹是不人性的,哈哈哈。

    2、RetroTag 接口,用于指示 Tag,当然这是对于初始化时设置了 Tag 的 API 请求。

    当然,如果你还是希望以基类包裹的方式,也是可以的,那就是在初始化的时候,不调用 setApiResultClass 方法就行了。

    另外,如果你不想增加 RetroTag 注解,也是可以的,那在调用的时候,就需要调用另一个方法,放入 Tag,如下:

    RxRetroHttp.create(Api2Service.class, "API2").getApi2Info()
    

    ApiResult

    现在,我们来看看 ApiResult。

    在 setApiResultClass 方法中传入的,是实现了 IApiResult 接口的请求返回基类,简单的样例代码如下

    public class Api2Result<T> implements IApiResult<T> {
        private int code;
        private String msg;
        private T result;
        
        @Override
        public boolean isSuccess(){
            return code == 1;
        }
        
        @Override
        public T getData(){
            return result;
        }
        
        @Override
        public String getResultMsg(){
            return msg;
        }
        
        @Override
        public String getResultCode(){
            return String.valueOf(code);
        }
        
        @Override
        public String getDataField(){
            return "result";
        }
    }
    

    其对应的返回 json 如下

    {
        code: 1,
        msg: "请求成功",
        result: {
            ...
        }
    }
    

    这是一个较为常用的 API 返回格式,而我们所要做的,就是实现几个基本方法,其中,isSuccess()返回的是请求成功的判断,getData()返回的是请求到的具体数据,getResultMsg()返回的是 API 请求信息,getResultCode()表示返回码,getDataField()返回的是 json 数据中表示具体数据的字段(在上面的 json 例子中,就是“ result ”)。

    更多配置

    Http 请求不可能没有相关的配置,而本框架并没有为大家内置很多配置方法,原因是,我认为这并不是本框架的主要功能。当然,大家也是可以进行自定义配置的,配置方式如下:

    RxRetroHttp.init(this).setXXX().setXXX();
    Retrofit.Builder retrofitBuilder = RxRetroHttp.getRetrofitBuilder();
    retrofitBuilder.setXXX().setXXX();
    OkHttpClient.Builder okHttpBuilder = RxRetroHttp.getOkHttpClientBuilder();
    okHttpBuilder.setXXX().setXXX();
    RxRetroHttp.getInstance().generateRetroClient();
    //RxRetroHttp.getInstance().generateRetroClient("YourTag")
    

    当然各套 API 请求之间的配置也是隔离的。框架也提供了一些简单的快捷配置方法,比如 addInterceptor、addNetworkInterceptor 等,更多的配置可以通过上述方式,获取 retrofitBuilder 和 okHttpBuilder 来配置。

    总结

    通过 Tag 的方式或许不是最好的方式,我也会继续尝试其他的方式,以对比便利性,如果大家有更好的方案提议,也希望能够通过提 issuer 的方式告诉我,感谢大家。

    更具体的使用方式,欢迎来 GitHub 仓库里的 Demo 查看,也欢迎和感谢各位 Star 支持。

    再次贴一下 Github 地址: https://github.com/BakerJQ/RxRetroHttp

    9 条回复    2019-01-29 18:59:13 +08:00
    kile
        1
    kile  
       2019-01-28 17:22:22 +08:00
    retrofit 在这种情况下违背了作者的思想...所以弄的很纠结...

    我的建议就是项目中有可能多 BaseUrl 的话,就别上 retrofit 了...

    这在非 retrofit 的网络请求工具中,无非就是拼个地址的事...
    theshyyyyy
        2
    theshyyyyy  
       2019-01-28 17:44:06 +08:00
    感觉使用场景有点罕见
    jellyEmmm
        3
    jellyEmmm  
       2019-01-29 10:40:38 +08:00
    https://github.com/JessYanCoding/RetrofitUrlManager

    了解一下,你这个代码侵入性太强了
    seselin
        4
    seselin  
       2019-01-29 11:26:19 +08:00
    求教,上面那张图片使用什么工具做的?
    BakerJ
        5
    BakerJ  
    OP
       2019-01-29 14:05:16 +08:00
    @jellyEmmm 那个我很早就看过了,他的只能够单纯的替换 url,通过 Intercepter+注解的方式。但是我所解决的问题是,除了 BaseUrl 之外,多套规则下返回解析、拦截处理、header、异常处理等机制都不同的情况。这样的话,即使用你发的这个,我也要在业务代码里,去指定返回结果等等很多其他的东西
    BakerJ
        6
    BakerJ  
    OP
       2019-01-29 14:06:05 +08:00
    @theshyyyyy 是非常特殊,但就是被我碰到了。。。。。。。无奈。。。。
    BakerJ
        7
    BakerJ  
    OP
       2019-01-29 14:06:52 +08:00
    @kile 不单单是 Url,而是解析规则、异常处理等很多都有多套
    BakerJ
        8
    BakerJ  
    OP
       2019-01-29 14:07:11 +08:00
    @seselin PS 呗
    twoyuan
        9
    twoyuan  
       2019-01-29 18:59:13 +08:00
    不知道是不是我没看懂楼主的需求……

    多个 Base URL 不是直接创建对应多个 Api 的 Service 吗,然后每个服务端的约定对应不同的拦截器就好了。如果 Base URL 也是通过接口获取的话,就在获取之后再创建这个 Api 的 Service 实例
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   4566 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 09:57 · PVG 17:57 · LAX 01:57 · JFK 04:57
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.