From RxJava to Coroutine

tags: Kotlin  RxJava  Coroutine

RxJava has been widely known and used as a streaming asynchronous framework. Now Kotlin provides us with a new choice, Coroutine, which can replace RxJava in some scenarios. This article compares the differences in the use of the two, hoping that users of RxJava can understand the advantages of Coroutine, which can be used as a new option in the project. This article is suitable for the following readers:

  • Have experience with Kotlin
  • Experience in using RxJava (RxKotlin)
  • Interested in trying Coroutine

Coroutine and Rx benchmarking

Before Flow came out, Coroutine could not be used to Stream data, so it could not replace Rx. The emergence of Flow makes up for the shortcomings, and Coroutine can basically completely replace the use of Rx. The powerful operators in Rx are also supported in Flow, such asmapfilterscanretryWait. RxObservableType can be replaced by Flow, RxSingleCompletableIt can be replaced by a simpler Coroutine processing method:

One-time data Stream data (Cold) Stream data (Hot)
RxJava Single/Maybe/Completable Observable Subject
Coroutine Suspend Functions Flow Channel

Through the above table, you can easily understand which types of data processing are used by Rx and Coroutine.


Single


Rx

Use Single in Rx when processing data that is only transmitted once, such as a single DB operation or Http request, etc.

fun getRequest(): Single<String> {
    return Single.create<String> { emitter ->
        // ...
        emitter.onSuccess(body.message)
    }
}

Coroutine

Coroutine can use the suspend function to return the result synchronously after performing any processing like a normal function, which is very refreshing:

suspend fun getRequest(): String{
    // Time-consuming processing
    return body.message
}

It should be noted that Single is a Cold Stream, which is triggered when subscribing, and the suspend function is not required. Therefore, scenarios that require Cold triggering must use Coroutine's Channel


Callback to Single


Rx

Sometimes we will convert a Callback into 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

Similarly, we can convert a Callback into Coroutine's suspend function:suspendCoroutineThe function is suspended after the call, and then passedresumeorresumeWithExceptionResume execution

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 does not return data directlyonCompleteWhen using Completable

fun updateRequest(): Completable {
    return Completable.create { emitter ->
        // ...
        emitter.onComplete()
    }
}

Coroutine

Return without returning data

suspend fun updateRequest() {
    // ...
    return
}

Observable.just()


Rx

Observable.just()Used to transmit one-time data, actually equivalent to Single

fun getMessage(): Observable<String> {
    return Observable.just("rxData")
}

Coroutine

can useflowOfreplaceObservable.just()

fun getMessage(): Flow<String> {
    return flowOf("flowData")
}

Observable.create()


Rx

When multiple data needs to be transmitted, useObservable.create()

fun getMessages(): Observable<String> {
    return Observable.create { observer ->
        for(/*Loop multiple times*/) {
            observer.onNext("rxData")
        }
        observer.onComplete()
    }
}

Coroutine

The flow block will be automatically called before exiting executioncompleted

fun getMessages(): Flow<String> {
    return flow {
        repeat(/*Loop multiple times*/) {
            emit("flowData")
        }
    }
}

Callback to Observable


Rx

You can convert a Callback with multiple callbacks into an Observable, throughonNextData will be sent in the form of 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 {
    	//Post-processing at the end of the 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 {
    	//Post-processing at the end of the stream
    }
    // Note that there should be no other code after awaitClose{...}
    /* End */
    
}

BehaviorSubject()


Rx

BehaviorSubject()Is a Hot Stream holding a recent data

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 mode)

suspend fun sample(): Int {
    return 1
}

//...

launch {
    sample() // want to call ordinary functions
}

Coroutine (Flow method)

fun getFlow(): Flow<Int> = flowOf(1)

//...

launch {
    getFlow().collect{ v ->
        // ...
    }
}

Coroutine (Channel method)

fun getChannel(): ReceiveChannel<Int> {
    return this.channel
} 

//...

launch {
    getChannel()
        .consumeAsFlow() // Convert Hot Stream to Cold Stream, which is equivalent to flow
        .collect { v ->
            // ...
        }
}

Serial processing


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 request
        emitter.onSuccess(message.body)
    }
}

fun insertDB(body: String): Completable {
    return Completable.create { emitter ->
        // Update 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 request
    return message.body
}

suspend fun insertDB(body: String) {
    // Insert DB
    return
}

//...
launch {
    getMessages().onEach { url ->
        val body = getRequest(rul)
        insertDB(body)
    }.collect {}
}

Parallel processing


Rx

byzipInitiate two Http requests at the same time

fun getHogeRequest(): Single<String> {
    return Single.create { emitter ->
        // HTTP request
        emitter.onSuccess(message.body)
    }
}

fun getFugaRequest(): Single<String> {
    return Single.create { emitter ->
        // HTTP request
        emitter.onSuccess(message.body)
    }
}

//...
Single.zip(getHogeRequest(), 
			getFugaRequest()).
			subscribeBy(
				onNext = {
				 	v: Pair<String, String> ->
				}
			)

Coroutine

asyncStart two Http requests at the same time, throughawaitAll()Wait for two requests to return results

suspend fun getHogeRequest(): String {
    // HTTP request
    return message.body
}

suspend fun getFugaRequest(): String {
    // HTTP request
    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()

to sum up


Finally, summarize the points of attention when Coroutine replaces Rx

  • Single and Completable can be replaced by suspend fun
  • Observable can be replaced by Flow
  • Subjects such as BehaviorSubject can use Channel instead

Each type in RxJava, whether it is Observable, Single or Subject, supports the same API: they all support the same operators, and they can all be subscribed.
In contrast to Coroutine, the suspend function, Flow or Channel are all based on the principle of function suspend, but the supported APIs are quite different. This may be a threshold in the use of Coroutine. In addition, you need to understandCoroutineScopeversusCoroutineContextSuch concepts are the basis for the smooth use of Coroutine.

Intelligent Recommendation

[RXJAVA] starting from the entrance

1: Core thinking The core thinking of RXJAVA1.0 and RXJAVA2.0 is the observer mode, but RXJAVA2.0 optimizes some methods in the basis of RXJAVA1.0, which is convenient for developers to better underst...

RXJAVA journey from scratch (1) ---- RXJAVA foundation

RxJava1 The open source library RXJAVA for the response programming is already fired for a long time. After being transplanted to a lot of platforms such as RXANDROID, it has been aware of it in recen...

Android uses Kotlin coroutine instead of RxJava wrapper network request

Today's Android projects generally use the combination of Retrofit+RxJava to implement network interface requests and data presentation. This feature is also easily implemented through the Kotlin lang...

Android Retrofit source code series (3) ~ Integrated RxJava, Coroutine analysis

Foreword Guide to Retrofit articles: Android Retrofit source code series (1) ~ Principle analysis Android Retrofit source code series (2) ~ custom CallAdapter Android Retrofit source code series (3) ~...

Coroutine, yield from, async await

#Use generator to implement coroutine Why is the generator? Because the generator is similar to the pause feature. Yield Look at a simple generator first. Generator generated and received values yield...

More Recommendation

Coroutine keyword yield and yield from

yield When the yield keyword appears in a function, then this function is a generator. You can iterate with a for loop or a next() function. Yield can receive variables passed in from the outside worl...

Foreach yield to talk to from coroutine

C # foreach is actually achieved through a process of continuous iterative set of outputs. It is a condition to be met   1 foreach object has GetEnumarator iterative method to return a collection...

Python from entry to mastery coroutine

Before we learned the concepts of threads and processes, we learned that in the operating system, processes are the smallest unit of resource allocation, and threads are the smallest unit of CPU sched...

Python and coroutine from Python2-Python3

Personal blog navigation page (clickRightlinkTo open the personal blog):Daniel takes you to the technology stack  Coroutine, also known as micro-thread, fiber, English name Coroutine; in one sent...

Python coroutine--yield and yield from

yield and yield from The dictionary gives two definitions for the verb "to yield": yield and concession. For yield in Python generators, both meanings are true. The yield item line of code w...

Copyright  DMCA © 2018-2026 - All Rights Reserved - www.programmersought.com  User Notice

Top