Skip to content

Instantly share code, notes, and snippets.

@stantronic
Last active May 27, 2022 09:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stantronic/3915e73f4a8faef59b7d22cb940ea292 to your computer and use it in GitHub Desktop.
Save stantronic/3915e73f4a8faef59b7d22cb940ea292 to your computer and use it in GitHub Desktop.

Rx Java to Coroutines Cheat Sheet

Some do's and dont's

Dont Do
Dont call suspend functions on view-models from activities or fragments Do launch coroutines in view-models from viewModelScope
call launch within a suspend function (unless you handle the error within the launch block) Do switch threads using withContext(dispatcher)
Dont call suspend functions on view-models from activities or fragments Do launch coroutines in view-models from viewModelScope
Dont call launch within a suspend function (unless you handle the error within the launch block) Do switch threads using withContext(dispatcher)
Dont use flow unless you need to. Rx's Single, Maybe and Completable can all be handled
using regular suspend calls Do use Flows or Channels if you need to replace an Observable or Flowable
Dont use Dispatchers.Main, Dispatchers.Default or Dispatchers.IO directly Do inject a CoroutineDispatcher using the relevant custom dagger annotation: e.g. @Ui, @Compute or @Io.
Retrofit uses its own dispatchers internally, so you dont normally need the @Io dispatcher for api calls. although you might need it for file-writing operations etc. Do inject the @Compute coroutine dispatcher into use-cases and repositories or anything requiring intensive computation.

Replacements

Instead of Use
someUseCase().subscribe { /* handle data */ } val data = someUseCase.await()
makeRxCall().subscribeOn(schedulers.io()) withContext(dispatcher){ makeRxCall().await() }
makeRxCall().subscribe({ handleSuccess(it)}, { handleError(it)}) try { /* make calls and handle success */ } catch(e: Throwable) { /* handle error */ }
doOnTerminate finally {}
PublishSubject<T>() MutableSharedFlow<T>()
exposing PublishSubject as Observable<T> expose MutableSharedFlow as SharedFlow<T>
BehaviourSubject<T>() MutableStateFlow<T>(defaultValue)
BehaviourSubject as Observable<T> expose mutable state flow as StateFlow<T>
instead of publishSubject.accept(value) mutableSharedFlow.emit(value) (or .tryEmit(value) if not in suspend call)

Putting it all together

// given a use-case like this:
interface SomeUseCase {
    operator fun invoke(): Single<SomeResult>
}

 // in a viewmodel using rx it might look like this:
fun getSingleResult() {

    someUseCase()
        .subscribeOn(schedulers.io())
        .doOnSubscribe { loading.postValue(true)  }
        .doOnTerminate { loading.postValue(false) }
        .subscribeBy(
            onSuccess = { result -> 
                liveData.postValue(result) 
            },
            onError = { 
                errors.postValue() 
            }
        )
        .addTo(susbcriptions)
}


// With co-routines it becomes:

fun getSingleResult() = viewModelScope.launch {
    loading.postValue(true)
    try {
        val result = someUseCase().await()
        liveData.postValue(result)
    } catch (e: Throwable) {
        errors.postValue(e)
    }
    finally {
        loading.postValue(false)
    }
}

Converting co-routines to Rx observables

When you need to convert in the opposite direction from suspend functions to RXJava classes:

  • If a suspend function returns nothing (Unit) then surround with rxCompletable { /* suspend call here */ }
  • If a suspend function returns a non-nullable type then surround with rxSingle { /* suspend call here */ }
  • If a suspend function returns a nullable type then either wrap it in an optional, or use a maybe:
    • rxMaybe { /* suspend call here */ }
    • rxSingle { Optional.of( /* suspend call here */ )
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment