Skip to content

Instantly share code, notes, and snippets.

@stantronic
Last active May 27, 2022 09:52
Show Gist options
  • Save stantronic/3915e73f4a8faef59b7d22cb940ea292 to your computer and use it in GitHub Desktop.
Save stantronic/3915e73f4a8faef59b7d22cb940ea292 to your computer and use it in GitHub Desktop.

Revisions

  1. stantronic revised this gist May 27, 2022. 1 changed file with 10 additions and 0 deletions.
    10 changes: 10 additions & 0 deletions Rx Java to Coroutines Cheat Sheet.md
    Original file line number Diff line number Diff line change
    @@ -73,3 +73,13 @@ fun getSingleResult() = viewModelScope.launch {
    }
    ```

    ## 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 */ )`

  2. stantronic revised this gist May 13, 2022. 1 changed file with 21 additions and 41 deletions.
    62 changes: 21 additions & 41 deletions Rx Java to Coroutines Cheat Sheet.md
    Original file line number Diff line number Diff line change
    @@ -2,52 +2,32 @@

    ## Some do's and dont's

    * _Dont_ call suspend functions on view-models from activities or fragments
    * _Do_ launch coroutines in view-models from viewModelScope
    | *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. |

    * _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
    * 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`.

    * _Do_ inject the `@Compute` coroutine dispatcher into use-cases and repositories
    or anything requiring intensive computation.
    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.


    ## Replacements

    * instead of `someUseCase().subscribe { /* handle data */ }`
    use `val data = someUseCase.await()`
    * instead of `makeRxCall().subscribeOn(schedulers.io())`
    use ` withContext(dispatcher){ makeRxCall().await() }`
    * instead of `makeRxCall().subscribe({ handleSuccess(it)}, { handleError(it)})`
    use `try { /* make calls and handle success */ } catch(e: Throwable) { /* handle error */ }`
    * instead of `doOnTerminate`
    use `finally {}`


    * instead of `PublishSubject<T>()`
    use `MutableSharedFlow<T>()`

    * instead of exposing PublishSubject as `Observable<T>`
    expose MutableSharedFlow as `SharedFlow<T>`

    * instead of `BehaviourSubject<T>()`
    use `MutableStateFlow<T>(defaultValue)`

    * instead of exposing BehaviourSubject as `Observable<T>`
    expose mutable state flow as `StateFlow<T>`

    * instead of `publishSubject.accept(value)`
    use `mutableSharedFlow.emit(value)` (or `.tryEmit(value)` if not in suspend call)
    | *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

  3. stantronic revised this gist May 13, 2022. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions Rx Java to Coroutines Cheat Sheet.md
    Original file line number Diff line number Diff line change
    @@ -22,6 +22,8 @@ Retrofit uses its own dispatchers internally, so you _dont_ normally need the `@
    for api calls; although you might need it for file-writing operations etc.


    ## Replacements

    * instead of `someUseCase().subscribe { /* handle data */ }`
    use `val data = someUseCase.await()`
    * instead of `makeRxCall().subscribeOn(schedulers.io())`
  4. stantronic revised this gist May 13, 2022. 1 changed file with 21 additions and 9 deletions.
    30 changes: 21 additions & 9 deletions Rx Java to Coroutines Cheat Sheet.md
    Original file line number Diff line number Diff line change
    @@ -22,19 +22,30 @@ Retrofit uses its own dispatchers internally, so you _dont_ normally need the `@
    for api calls; although you might need it for file-writing operations etc.


    instead of `someUseCase().subscribe { /* handle data */ }` use `val data = someUseCase.await()`
    instead of `makeRxCall().subscribeOn(schedulers.io())`
    * instead of `someUseCase().subscribe { /* handle data */ }`
    use `val data = someUseCase.await()`
    * instead of `makeRxCall().subscribeOn(schedulers.io())`
    use ` withContext(dispatcher){ makeRxCall().await() }`
    instead of `makeRxCall().subscribe({ handleSuccess(it)}, { handleError(it)})`
    * instead of `makeRxCall().subscribe({ handleSuccess(it)}, { handleError(it)})`
    use `try { /* make calls and handle success */ } catch(e: Throwable) { /* handle error */ }`
    instead of `doOnTerminate` use `finally {}`
    * instead of `doOnTerminate`
    use `finally {}`


    instead of `PublishSubject<T>()` use `MutableSharedFlow<T>()`
    instead of exposing PublishSubject as `Observable<T>` expose MutableSharedFlow as `SharedFlow<T>`
    instead of `BehaviourSubject<T>()` use `MutableStateFlow<T>(defaultValue)`
    instead of exposing BehaviourSubject as `Observable<T>` expose mutable state flow as `StateFlow<T>`
    instead of `publishSubject.accept(value)` use `mutableSharedFlow.emit(value)` (or `.tryEmit(value)` if not in suspend call)
    * instead of `PublishSubject<T>()`
    use `MutableSharedFlow<T>()`

    * instead of exposing PublishSubject as `Observable<T>`
    expose MutableSharedFlow as `SharedFlow<T>`

    * instead of `BehaviourSubject<T>()`
    use `MutableStateFlow<T>(defaultValue)`

    * instead of exposing BehaviourSubject as `Observable<T>`
    expose mutable state flow as `StateFlow<T>`

    * instead of `publishSubject.accept(value)`
    use `mutableSharedFlow.emit(value)` (or `.tryEmit(value)` if not in suspend call)

    ## Putting it all together

    @@ -65,6 +76,7 @@ fun getSingleResult() {


    // With co-routines it becomes:

    fun getSingleResult() = viewModelScope.launch {
    loading.postValue(true)
    try {
  5. stantronic created this gist May 13, 2022.
    81 changes: 81 additions & 0 deletions Rx Java to Coroutines Cheat Sheet.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,81 @@
    # Rx Java to Coroutines Cheat Sheet

    ## Some do's and dont's

    * _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
    * 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`.

    * _Do_ inject the `@Compute` coroutine dispatcher into use-cases and repositories
    or anything requiring intensive computation.
    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.


    instead of `someUseCase().subscribe { /* handle data */ }` use `val data = someUseCase.await()`
    instead of `makeRxCall().subscribeOn(schedulers.io())`
    use ` withContext(dispatcher){ makeRxCall().await() }`
    instead of `makeRxCall().subscribe({ handleSuccess(it)}, { handleError(it)})`
    use `try { /* make calls and handle success */ } catch(e: Throwable) { /* handle error */ }`
    instead of `doOnTerminate` use `finally {}`


    instead of `PublishSubject<T>()` use `MutableSharedFlow<T>()`
    instead of exposing PublishSubject as `Observable<T>` expose MutableSharedFlow as `SharedFlow<T>`
    instead of `BehaviourSubject<T>()` use `MutableStateFlow<T>(defaultValue)`
    instead of exposing BehaviourSubject as `Observable<T>` expose mutable state flow as `StateFlow<T>`
    instead of `publishSubject.accept(value)` use `mutableSharedFlow.emit(value)` (or `.tryEmit(value)` if not in suspend call)

    ## Putting it all together

    ```kotlin

    // 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)
    }
    }
    ```