RxJava作为流式异步框架早已被广泛熟知和使用,如今Kotlin又为我们提供了一个新的选择Coroutine、在一些场景下可以替代RxJava的使用。本文通过比较两者在使用上的不同,希望让RxJava的使用者们能了解到Coroutine的优点,可以作为一个新的选项在项目中应用。本文适合以下读者:
- 有Kotlin的使用经验
- 有RxJava(RxKotlin)的使用经验
- 有兴趣尝试Coroutine
Coroutine与Rx的对标
Flow出来以前,Coroutine不能用来Stream数据,所以无法替代Rx。Flow的出现弥补了填补了短板,似的Coroutine已经基本上可以完全替代Rx的使用。Rx中强大的操作符在Flow中也得到了支持,例如map
、filter
、scan
、retry
等。Rx的Observable
类型可以使用Flow替代,Rx的Single
、Completable
可以更简单的Coroutine处理方式替代:
一次性数据 | Stream数据(Cold) | Stream数据(Hot) | |
---|---|---|---|
RxJava | Single/Maybe/Completable | Observable | Subject |
Coroutine | Suspend Functions | Flow | Channel |
通过上表可以简单了解Rx与Coroutine在数据处理方面分别用到哪些类
Single
Rx
Rx中使用Single处理只发射一次的数据时使用Single,例如一个单次DB的操作或者Http请求等
fun getRequest(): Single<String> {
return Single.create<String> { emitter ->
// ...
emitter.onSuccess(body.message)
}
}
Coroutine
Coroutine可以使用suspend函数,像普通函数一样进行任何处理后同步返回结果,非常清爽:
suspend fun getRequest(): String{
// 耗时处理
return body.message
}
需要注意Single是Cold Stream的,当订阅时才触发,suspend函数则不需要,所以需要Cold触发的场景,要借助Coroutine的Channel
Callback转Single
Rx
有时我们会将一个Callback转成Rx的Single
fun getRequest(): Single<String> {
return Single.create<String> { emitter ->
val callback = object: Callback {
override fun onNextValue(value: String) {
emitter.onSuccess(value)
}
override fun onApiError(cause: Throwable) {
emitter.onFailure(Exception("API Error", cause))
}
}
}
}
Coroutine
同样,我们可以将一个Callback转成Coroutine的suspend函数:suspendCoroutine
调用后函数被挂起,再通过resume
或者resumeWithException
恢复执行
suspend fun getMessages(): String {
return suspendCoroutine<String> { coroutine ->
val callback = object: Callback {
override fun onNextValue(value: String) {
coroutine.resume(value)
}
override fun onApiError(cause: Throwable) {
coroutine.resumeWithException(Exception("API Error", cause))
}
}
}
}
Completable
Rx
Rx不返回数据直接onComplete
时,使用Completable
fun updateRequest(): Completable {
return Completable.create { emitter ->
// ...
emitter.onComplete()
}
}
Coroutine
不返回数据直接return
suspend fun updateRequest() {
// ...
return
}
Observable.just()
Rx
Observable.just()
用来发射一次性数据,实际相当于Single
fun getMessage(): Observable<String> {
return Observable.just("rxData")
}
Coroutine
可以使用flowOf
替换Observable.just()
fun getMessage(): Flow<String> {
return flowOf("flowData")
}
Observable.create()
Rx
当需要发射多个数据时,使用Observable.create()
fun getMessages(): Observable<String> {
return Observable.create { observer ->
for(/*循环多次*/) {
observer.onNext("rxData")
}
observer.onComplete()
}
}
Coroutine
flow的block退出执行之前会自动调用completed
fun getMessages(): Flow<String> {
return flow {
repeat(/*循环多次*/) {
emit("flowData")
}
}
}
Callback转Observable
Rx
可以将一个多次回调的Callback转成一个Observable,通过onNext
将以Stream的形式发送数据
fun getMessages(): Observable<String> {
return Observable.create { observer ->
val callback = object: Callback {
override fun onNextValue(value: String) {
observer.onNext(value)
}
override fun onApiError(cause: Throwable) {
observer.onError(Exception("API Error", cause))
}
override fun onComplete() = observer.onComplete()
}
}.doFinally {
//stream结束时的后处理
}
}
Coroutine
fun getMessages(): Flow<String> {
return callbackFlow {
val callback = object: Callback {
override fun onNextValue(value: String) {
offer(value)
}
override fun onApiError(cause: Throwable) {
cancel(Exception("API Error", cause))
}
override fun onComplete() = channel.close()
}
}
awaitClose {
//stream结束时的后处理
}
// 注意awaitClose{...}之后不应该有其他代码
/* End */
}
BehaviorSubject()
Rx
BehaviorSubject()
是持有一个最近数据的Hot Stream
class SampleClass {
private val behaviorSubject: BehaviorSubject<Int> = BehaviorSubject.create(0)
}
Coroutine
class SampleClass {
private val channel: Channel<Int> = Channel(Channel.CONFLATED)
}
ReplaySubject()
Rx
class SampleClass {
private val replaySubject: ReplaySubject<Int> = ReplaySubject.create()
}
Coroutine
class SampleClass {
private val channel: Channel<Int> = Channel(Channel.UNLIMTED)
}
Subscribe()
Rx
Single.just(1).subscribeBy(
onNext = { v ->
// ...
}
)
Coroutine(suspend方式)
suspend fun sample(): Int {
return 1
}
//...
launch {
sample() // 想调用普通函数一样
}
Coroutine(Flow方式)
fun getFlow(): Flow<Int> = flowOf(1)
//...
launch {
getFlow().collect{ v ->
// ...
}
}
Coroutine(Channel方式)
fun getChannel(): ReceiveChannel<Int> {
return this.channel
}
//...
launch {
getChannel()
.consumeAsFlow() // 将Hot Stream转成 Cold Stream, 相当于flow
.collect { v ->
// ...
}
}
串行处理
Rx
fun getMessages(): Observable<String> {
return Observable.create<String> { observer ->
val callback = object: Callback {
override fun onNextValue(value: String) {
observer.onNext(value)
}
override fun onApiError(cause: Throwable) {
observer.onError(Exception("API Error", cause))
}
override fun onComplete() = observer.onComplete()
}
}.doFinally {/*...*/}
}
fun getRequest(url: String): Single<String> {
return Single.create { emitter ->
// http请求
emitter.onSuccess(message.body)
}
}
fun insertDB(body: String): Completable {
return Completable.create { emitter ->
// 更新DB
emitter.onComplete()
}
}
//...
getMessages()
.flatMapSingle { getRequest(it) }
.flatMapCompletable { insertDB(it) }
.subscribeBy(onComplete = {})
Coroutine
fun getMessages(): Flow<String> {
return callbackFlow {
val callback = object : Callback {
override fun onNextValue(value: String) {
offer(value)
}
override fun onApiError(cause: Throwable) {
cancel(Exception("API Error", cause))
}
override fun onComplete() = channel.close()
}
}
awaitClose {/*...*/}
}
suspend fun getRequest(url: String): String {
// http请求
return message.body
}
suspend fun insertDB(body: String) {
// 插入DB
return
}
//...
launch {
getMessages().onEach { url ->
val body = getRequest(rul)
insertDB(body)
}.collect {}
}
并行处理
Rx
通过zip
同时发起两个Http请求
fun getHogeRequest(): Single<String> {
return Single.create { emitter ->
// HTTP请求
emitter.onSuccess(message.body)
}
}
fun getFugaRequest(): Single<String> {
return Single.create { emitter ->
// HTTP请求
emitter.onSuccess(message.body)
}
}
//...
Single.zip(getHogeRequest(),
getFugaRequest()).
subscribeBy(
onNext = {
v: Pair<String, String> ->
}
)
Coroutine
async
同时启动两个Http请求,通过awaitAll()
等待两个请求返回结果
suspend fun getHogeRequest(): String {
// HTTP请求
return message.body
}
suspend fun getFugaRequest(): String {
// HTTP请求
return message.body
}
//...
launch {
val resultList: List<String> = listOf(
async{ getHogeRequest() },
async{ getFugaRequest() })
.awaitAll()
}
Dispose
Rx
val disposable = hogeObservable().subscribeBy(onNext = {})
disposable.dispose()
Coroutine
val job = launch {
hogeFlow.collect {}
}
job.cancel()
总结
最后总结一下Coroutine替代Rx时的注意点
- Single和Completable可以使用suspend fun替代
- Observable可以使用Flow替代
- BehaviorSubject等Subject可以使用Channel替代
RxJava中的各个类型,无论是Observable、Single还是Subject,其支持的API都差不多:都支持同样的操作符,都可以进行subscribe。
反观Coroutine,suspend函数、Flow或者Channel 虽然都是基于函数挂起的原理运行,但是支持的API确大相径庭,这可能是Coroutine使用中的一点门槛。另外,还需要需要理解CoroutineScope
与CoroutineContext
等概念,这都是顺利使用Coroutine的基础。