RxHttp 优雅的实现请求串行与并行

10,134 阅读7分钟

前言

现实开发中,一个页面很少只有一个请求的,一般都有多个请求,有的需要串行,有的需要并行,使用传统的方法,如果有n个接口,我们就要设置n个接口回调,如果是串行的话,还需要在当前接口成功或失败的地方,调用下个一个请求,一个接着一个,真的是要逼死强迫症患者,而且代码可读性非常的差,新人来了往往要看上半天,不好维护且容易出错。

本文使用RxHttp请求框架作为案例演示,如果你不了解RxHttp,请查看30秒上手新一代Http请求神器RxHttp

请容许我再唠两句:RxHttp从4月中旬开始推广,在大家都对新的Http请求框架学不动或者懒得学的情况下,RxHttp依然收获了一大波粉丝,目前在Github上一经有415颗星,其中

RxHttp 让你眼前一亮的Http请求框架

Android 史上最优雅的实现文件上传、下载及进度的监听

这两篇文章更是得到了得到「玉刚说」及「刘望舒」微信公众号独家原创发布,我想,这也是对RxHttp的一种肯定,欢迎大家体验RxHttp,它优雅的写法及强大的功能,相信你一定会爱上它。

gradle依赖

  • OkHttp 3.14.x以上版本, 最低要求为API 21,如你想要兼容21以下,请依赖OkHttp 3.12.x,该版本最低要求 API 9

  • asXxx方法内部是通过RxJava实现的,而RxHttp 2.2.0版本起,内部已剔除RxJava,如需使用,请自行依赖RxJava并告知RxHttp依赖的Rxjava版本

必须

jitpack添加到项目的build.gradle文件中,如下:

allprojects {
    repositories {
        maven { url "https://jitpack.io" }
    }
}

注:RxHttp 2.6.0版本起,已全面从JCenter迁移至jitpack

//使用kapt依赖rxhttp-compiler时必须
apply plugin: 'kotlin-kapt'

android {
    //必须,java 8或更高
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation 'com.github.liujingxing.rxhttp:rxhttp:2.6.0'
    implementation 'com.squareup.okhttp3:okhttp:4.9.0' //rxhttp v2.2.2版本起,需要手动依赖okhttp
    kapt 'com.github.liujingxing.rxhttp:rxhttp-compiler:2.6.0' //生成RxHttp类,纯Java项目,请使用annotationProcessor代替kapt
 }

可选

android {
    defaultConfig {
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [
                    rxhttp_package: 'rxhttp',   //非必须,指定RxHttp类包名
                    //传入你依赖的rxjava版本,可传入rxjava2、rxjava3,依赖RxJava时必须
                    rxhttp_rxjava: 'rxjava3'

                ]
            }
        }
    }
}
dependencies {
    implementation 'com.github.liujingxing.rxlife:rxlife-coroutine:2.1.0' //管理协程生命周期,页面销毁,关闭请求
    
    //rxjava2   (RxJava2/Rxjava3二选一,使用asXxx方法时必须)
    implementation 'io.reactivex.rxjava2:rxjava:2.2.8'
    implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
    implementation 'com.github.liujingxing.rxlife:rxlife-rxjava2:2.1.0' //管理RxJava2生命周期,页面销毁,关闭请求

    //rxjava3
    implementation 'io.reactivex.rxjava3:rxjava:3.0.6'
    implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
    implementation 'com.github.liujingxing.rxlife:rxlife-rxjava3:2.1.0' //管理RxJava3生命周期,页面销毁,关闭请求

    //非必须,根据自己需求选择 RxHttp默认内置了GsonConverter
    implementation 'com.github.liujingxing.rxhttp:converter-fastjson:2.6.0'
    implementation 'com.github.liujingxing.rxhttp:converter-jackson:2.6.0'
    implementation 'com.github.liujingxing.rxhttp:converter-moshi:2.6.0'
    implementation 'com.github.liujingxing.rxhttp:converter-protobuf:2.6.0'
    implementation 'com.github.liujingxing.rxhttp:converter-simplexml:2.6.0'
}

注:添加依赖后,需要rebuild一下项目,注解处理器才会生成RxHttp类; 另外kotlin用户,请使用kapt替代annotationProcessor

最后,Rebuild一下项目(必须的),就会自动生成RxHttp类

以上步骤后,还未生成RxHttp类,请查看RxHttp类没有生成,检查步骤

接下来,我们正式开始。

并行

现在很多页面是这样的,上面是Banner条,Banner条下面是数据列表(假设是学生列表),这样就涉及到两个接口,一个是获取Banner条数据,另一是获取学生列表数据,这两个接口没有任何关系,所以我们可以并行去实现

//Banner 的Observable对象                                                
Observable<Banner> bannerObservable = RxHttp.get("http://...")        
    .asClass(Banner.class);                                          
                                                                      
//学生的Observable对象                                                     
Observable<List<Student>> studentObservable = RxHttp.get("http://...")
    .asList(Student.class);            
                                                                      
//这里使用RxJava组合符中的merge操作符,将两个被观察者合并为一个                                
Observable.merge(bannerObservable, studentObservable)                 
    .as(RxLife.asOnMain(this)) //感知生命周期,自动关闭请求                        
    .subscribe(o -> {                                                 
        //请求成功,回调2次,一次是Banner数据,一次Student列表                   
        if (o instanceof Banner) {                                    
            //获取到banner数据                                             
        } else if (o instanceof List) {                               
            //获取到学生列表数据                                               
        }                                                             
    }, throwable -> {                                                 
        //出现异常                                                        
    }, () -> {                                                        
        //2个请求执行完毕,开始更新UI                                             
    });                                                               

可以看到,我们首先通过RxHttp类拿到Banner和Student的两个Observable对象,然后通过merge操作符,将两个Observable对象合并为一个,并订阅观察者,这样就能在onNext回调中拿到Banner和Student数据,并在onComplete回调中更新UI。

可是,这样就完了吗?熟悉RxJava的同学应该知道,RxJava在出现异常后并且回调到onError接口时,就会停止工作,那么如果Banner接口先出现异常,岂不是收不到Student信息了?是的,那么,我们应该如何去处理呢,其实很简单,RxJava为我们提供了异常捕获操作符,如:onErrorResumeNextonErrorReturn,作用就是出现异常了,我们如何去补救它。如果你不了解RxJava错误处理机制,请查看RxJava错误处理详解。这里,我们使用onErrorResumeNext操作符,代码如下

//Banner 的Observable对象                                                
Observable<Banner> bannerObservable = RxHttp.get("http://...")        
    .asClass(Banner.class)
    .onErrorResumeNext(Observable.empty()); //出现异常,发送一个空的Observable对象                                         
                                                                      
//学生的Observable对象                                                     
Observable<List<Student>> studentObservable = RxHttp.get("http://...")
    .asList(Student.class);            
                                                                      
//这里使用RxJava组合符中的merge操作符,将两个被观察者合并为一个                                
Observable.merge(bannerObservable, studentObservable)                 
    .as(RxLife.asOnMain(this)) //感知生命周期,自动关闭请求                        
    .subscribe(o -> {                                                 
        //请求成功,回调2次,一次是Banner数据,一次Student列表                   
        if (o instanceof Banner) {                                    
            //获取到banner数据                                             
        } else if (o instanceof List) {                               
            //获取到学生列表数据                                               
        }                                                             
    }, throwable -> {                                                 
        //出现异常                                                        
    }, () -> {                                                        
        //2个请求执行完毕,开始更新UI                                             
    });

上面我们只加了onErrorResumeNext(Observable.empty())这一行代码,Observable.empty()是一个不会发射任何事件的Observable对象。所以,这个时候如果Banner的Observable出现异常,就不会发射任何事件,Student 的Observable对象便可继续执行,只是在onNext回调中,就只能收到一次Student的回调(请求成功的话),并且随后执行onComplete回调更新UI,这样就能保证即使Banner接口出错了,我们依然可以正常现实学生列表数据。说的抽象一点就是保证A接口不影响B接口,但是B可以影响A接口,如果要保证A、B两个接口互不影响,分别对A、B接口处理异常即可,如果有3个、4个甚至更多的请求,可以使用Observable.mergeArray操作符。

串行

接下来,看看我们串行,假设我们有这样一个需求,需要在注册完成后立即去登录,这种情况下,就只能串行去实现,在这,我们使用RxJava的flatMap这个操作符去实现

flatMap

RxHttp.postForm("http://...") //发送注册请求
    .add("userName", "zhangsan")
    .add("password", "123456")
    .asClass(Register.class)
    .flatMap((io.reactivex.functions.Function<Register, ObservableSource<User>>) register -> {
        //注册成功,拿到注册信息去登录,并返回User对象
        return RxHttp.get("http://...") //发送登录请求
                .add("userId", register.getUserId())
                .add("password", register.getPassword())
                .setSync()      //同步请求
                .asClass(User.class);
    })
    .as(RxLife.asOnMain(this)) //感知生命周期,自动关闭请求
    .subscribe(user -> {
        //注册并且登录成功,拿到用户信息
    }, throwable -> {
        //出现异常,注册失败或者登录失败
    });

注:RxHttp中的asXXX系列方法,内部会默认开启IO线程执行Http请求,所以我们在发送单个请求时,无需指定请求执行线程;然而在多个请求串行时,为提升效率,我们希望一个线程可以执行多个请求,故我们需要使用setSync方法指定在当前线程发起同步请求。

可以看到,这里我们使用flatMap操作符,当注册成功,就会走到flatMap内部去登录,登录成功就会拿到User对象并回调观察者。

小结

看完你会发现,RxHttp做到了与RxJava的无缝连接,使用asXXX系列方法,就可以拿到一个Observable<T>对象,随后再结合RxJava的mergeflatMap,就可以优雅的实现的Http请求的串行及并行。如果你对RxJava有一定的了解,还可以实现很多有意思的功能,比如:为单个请求设置超时、请求失败自动重试n次等等。

最后,一切功劳都要归功于RxJava的强大,感谢RxJava,向它致敬!!!!