Upgrade to Pro — share decks privately, control downloads, hide ads and more …

mDevCamp: RxJava & Coroutines: A Practical Analysis

mDevCamp: RxJava & Coroutines: A Practical Analysis

Kotlin has taken the Android world by storm, and is quickly becoming the most popular language, with coroutines now stable, does it make sense to replace your RxJava implementations for Coroutines?

Despite the hype, it may not make sense to jump on the bandwagon just yet, with RxJava having already proven its stability and usefulness, and in many cases the comparison between the two frameworks showing that they simply fit different purposes.

In this talk, you can learn how you can utilise the strengths of each framework, how to correctly choose the best solution for the requirements of your project, whether it may be beneficial to migrate or run them concurrently, and how you can start doing so for your project.

Ash Davies

May 31, 2019
Tweet

More Decks by Ash Davies

Other Decks in Programming

Transcript

  1. RxJava & Coroutines
    A Practical Analysis
    @askashdavies

    View Slide

  2. @askashdavies

    View Slide

  3. @askashdavies

    View Slide

  4. pusher.com/state-of-kotlin

    View Slide

  5. blog.jetbrains.com/kotlin/2016/02/kotlin-1-0-released-pragmatic-language-for-jvm-and-android/

    View Slide

  6. Coroutines
    @askashdavies

    View Slide

  7. fun main() {
    GlobalScope.launch {
    delay(1000L)
    println("World!")
    }
    println("Hello,")
    Thread.sleep(2000L)
    }
    // Hello,
    // World!
    @askashdavies

    View Slide

  8. fun main() {
    GlobalScope.launch {
    delay(1000L)
    println("World!")
    }
    println("Hello,")
    Thread.sleep(2000L)
    }
    // Hello,
    // World!
    @askashdavies

    View Slide

  9. fun main() {
    GlobalScope.launch {
    delay(1000L)
    println("World!")
    }
    println("Hello,")
    Thread.sleep(2000L)
    }
    // Hello,
    // World!
    @askashdavies

    View Slide

  10. Coroutine Builders
    @askashdavies

    View Slide

  11. Coroutine Builders
    val deferred: Deferred = async { "Hello World!" }
    @askashdavies

    View Slide

  12. Coroutine Builders
    val deferred: Deferred = async { "Hello World!" }
    val result: String = deferred.await()
    @askashdavies

    View Slide

  13. Coroutine Builders
    val deferred: Deferred = async { "Hello World!" }
    val result: String = deferred.await()
    val job: Job = launch { "Hello World!" }
    @askashdavies

    View Slide

  14. Coroutine Builders
    val deferred: Deferred = async { "Hello World!" }
    val result: String = deferred.await()
    val job: Job = launch { "Hello World!" }
    job.join()
    @askashdavies

    View Slide


  15. Stability
    @askashdavies

    View Slide

  16. @askashdavies

    View Slide

  17. @Annotations
    (
    !
    Here be dragons)
    @askashdavies

    View Slide

  18. Annotations
    @ExperimentalCoroutinesApi //
    @askashdavies

    View Slide

  19. Annotations
    @ExperimentalCoroutinesApi //
    @ObsoleteCoroutinesApi //

    @askashdavies

    View Slide

  20. Annotations
    @ExperimentalCoroutinesApi //
    @ObsoleteCoroutinesApi //

    @InternalCoroutinesApi //

    @askashdavies

    View Slide

  21. !
    Coroutines
    @askashdavies

    View Slide

  22. !
    Native first-party library
    @askashdavies

    View Slide

  23. !
    Easy-to-use
    @askashdavies

    View Slide

  24. !
    suspend fun
    @askashdavies

    View Slide

  25. Dispatchers
    @askashdavies

    View Slide

  26. !
    History of Android
    @askashdavies

    View Slide

  27. Background Processes
    @askashdavies

    View Slide

  28. Background Processes
    !
    Runnable / Handler
    @askashdavies

    View Slide

  29. Background Processes !
    AsyncTask
    @askashdavies

    View Slide

  30. Background Processes
    !
    IntentService
    @askashdavies

    View Slide

  31. Background Processes !
    Loader
    @askashdavies

    View Slide

  32. Background Processes
    WorkManager
    @askashdavies

    View Slide

  33. !
    @askashdavies

    View Slide

  34. xkcd.com/927/

    View Slide

  35. RxJava to the rescue
    © Ubisoft

    View Slide

  36. !
    Reactive
    @askashdavies

    View Slide

  37. @askashdavies

    View Slide

  38. @askashdavies

    View Slide

  39. @askashdavies

    View Slide

  40. @askashdavies

    View Slide

  41. @askashdavies

    View Slide

  42. !
    Asynchronous APIs
    Synchronous APIs
    @askashdavies

    View Slide

  43. @askashdavies

    View Slide

  44. !
    Observable
    .fromIterable(resourceDraft.getResources())
    .flatMap(resourceServiceApiClient::createUploadContainer)
    .zipWith(Observable.fromIterable(resourceDraft.getResources()), Pair::create)
    .flatMap(uploadResources())
    .toList()
    .toObservable()
    .flatMapMaybe(resourceCache.getResourceCachedItem())
    .defaultIfEmpty(Resource.getDefaultItem())
    .flatMap(postResource(resourceId, resourceDraft.getText(), currentUser, resourceDraft.getIntent()))
    .observeOn(AndroidSchedulers.mainThread())
    .subscribeOn(Schedulers.io())
    .subscribe(
    resource -> repository.setResource(resourceId, resource, provisionalResourceId),
    resourceUploadError(resourceId, resourceDraft, provisionalResourceId)
    );
    @askashdavies

    View Slide

  45. Anchorman: The Legend of Ron Burgundy (DreamWorks Pictures)

    View Slide


  46. Hidden complexity
    @askashdavies

    View Slide

  47. !
    Hidden gotchas
    @askashdavies

    View Slide

  48. !
    Memory footprint
    @askashdavies

    View Slide


  49. Steep learning curve
    @askashdavies

    View Slide

  50. View Slide

  51. #RxMustDie
    pca.st/7IJG
    @askashdavies

    View Slide

  52. "If all you have is a hammer,
    everything looks like a nail"
    — Abraham Maslow, The Psychology of Science, 1966
    @askashdavies

    View Slide

  53. Coroutine Use Cases
    @askashdavies

    View Slide

  54. Network Call Handling
    // RxJava2: Single
    fun getUser(): Single = Single.fromCallable { /* ... */ }
    // Coroutines: T
    suspend fun getUser(): User = /* ... */
    @askashdavies

    View Slide

  55. Cache Retrieval
    // RxJava2: Maybe
    fun getUser(): Maybe = Maybe.fromCallable { /* ... */ }
    // Coroutines: T?
    suspend fun getUser(): User? = /* ... */
    @askashdavies

    View Slide

  56. Background Task Handling
    // RxJava2: Completable
    fun storeUser(user: User): Completable.fromCallable { /* ... */ }
    // Coroutines: Unit
    suspend fun storeUser(user: User) { /* ... */ }
    @askashdavies

    View Slide

  57. Thread Handling
    // RxJava2
    getUser()
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe { /* Do something */ }
    // Coroutines
    launch(Dispatchers.Main) {
    withContext(Dispatchers.IO) {
    val user = getUser()
    /* Do something */
    }
    }
    @askashdavies

    View Slide

  58. Task Cancellation
    // RxJava2
    val disposable: Disposable = Single
    .create { /* Do something */ }
    .subscribe { /* Do something else */ }
    disposable.dispose()
    // Coroutine
    val parent: Job = Job()
    launch(Dispatchers.Main + parent) { /* Do something */ }
    parent.cancelChildren()
    @askashdavies

    View Slide

  59. ViewModel.viewModelScope
    androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0-beta01
    @askashdavies

    View Slide

  60. Channels
    @askashdavies

    View Slide

  61. Roman Elizarov
    Cold flows, hot channels
    Flow.collect {
    it.emit("Hello World!")
    }
    medium.com/@elizarov/cold-flows-hot-channels-d74769805f9

    View Slide


  62. RxJava: Complex Business Logic
    @askashdavies

    View Slide

  63. Asynchronicity Comparison
    Manuel Vivo (@manuelvicnt)
    medium.com/capital-one-tech/coroutines-and-rxjava-an-asynchronicity-comparison-part-1-asynchronous-programming-e726a925342a

    View Slide

  64. Is Coroutines a replacement for RxJava?
    @askashdavies

    View Slide

  65. Maybe...
    @askashdavies

    View Slide

  66. Not really...
    @askashdavies

    View Slide

  67. Should I migrate to Coroutines?
    @askashdavies

    View Slide

  68. Probably not...
    @askashdavies

    View Slide

  69. "If it ain't broke, don't fix it"
    — Bert Lance, Nation's Business, 1977
    @askashdavies

    View Slide

  70. © 2019 Viacom International Inc.

    View Slide

  71. Did "you" migrate to Coroutines?
    @askashdavies

    View Slide

  72. !
    Yes!
    @askashdavies

    View Slide

  73. How could I migrate to Coroutines?
    @askashdavies

    View Slide

  74. !
    Migration Policy
    @askashdavies

    View Slide

  75. Retrofit Services
    @askashdavies

    View Slide

  76. Retrofit2 Coroutines Adapter
    com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:+
    github.com/JakeWharton/retrofit2-kotlin-coroutines-adapter

    View Slide

  77. interface UserService {
    @GET("/user")
    fun getUser(): Deferred
    }
    val retrofit = Retrofit.Builder()
    .baseUrl("https://example.com/")
    .addCallAdapterFactory(CoroutineCallAdapterFactory())
    .build()
    GlobalScope.launch {
    val user = retrofit
    .create() // >= 2.5.0
    .getUser()
    .await()
    }
    @askashdavies

    View Slide

  78. interface UserService {
    @GET("/user")
    fun getUser(): Deferred
    }
    val retrofit = Retrofit.Builder()
    .baseUrl("https://example.com/")
    .addCallAdapterFactory(CoroutineCallAdapterFactory())
    .build()
    GlobalScope.launch {
    val user = retrofit
    .create() // >= 2.5.0
    .getUser()
    .await()
    }
    @askashdavies

    View Slide

  79. Retrofit
    First-party "suspend" support
    github.com/square/retrofit/pull/2886

    View Slide

  80. Coroutines RxJava2
    org.jetbrains.kotlinx:kotlinx-coroutines-rx2:+
    @askashdavies

    View Slide

  81. Name Scope Description
    rxCompletable CoroutineScope Cold completable that starts
    coroutine on subscribe
    rxMaybe CoroutineScope Cold maybe that starts coroutine
    on subscribe
    rxSingle CoroutineScope Cold single that starts coroutine
    on subscribe
    rxObservable ProducerScope Cold observable that starts
    coroutine on subscribe
    rxFlowable ProducerScope Cold observable that starts
    coroutine on subscribe with
    backpressure support
    @askashdavies

    View Slide

  82. val service: UserService = retrofit.create() // >= 2.5.0
    GlobalScope
    .rxSingle { service.getUser() }
    .observeOn(AndroidSchedulers.mainThread())
    .subscribeOn(Schedulers.io())
    .subscribe(
    { /* Do something with user */ },
    { /* Handle error ... probably */ }
    )
    @askashdavies

    View Slide

  83. Completable.await()
    GlobalScope.launch {
    Completable
    .complete()
    .await()
    }
    @askashdavies

    View Slide

  84. Maybe.await()
    GlobalScope.launch {
    val result: String? = Maybe
    .fromCallable { null as String? }
    .await()
    // result == null
    }
    @askashdavies

    View Slide

  85. Observable.await...
    val observable = Observable.just(1, 2, 3, 4)
    // Await first item
    val item = observable.awaitFirst()
    // Print each item
    observable.consumeEach(::println)
    // Consume all items
    observable
    .openSubscription()
    .consume {
    println(it.size)
    }
    kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/io.reactivex.-observable-source/index.html

    View Slide

  86. Exceptions?
    @askashdavies

    View Slide

  87. Exceptions
    observable.subscribe(
    onSuccess = { it: T -> },
    onError = { it: Throwable -> } // Gotta catch em all!
    )
    launch {
    try {
    doSomethingDangerous()
    } catch(exception: DangerousException) {
    // Specific exception caught!
    }
    }
    @askashdavies

    View Slide

  88. Exceptions
    val handler = CoroutineExceptionHandler { _, exception ->
    println("Caught $exception")
    }
    val job = GlobalScope.launch(handler) {
    throw AssertionError()
    }
    join(job)
    // Caught java.lang.AssertionError
    @askashdavies

    View Slide

  89. !
    Conclusion
    @askashdavies

    View Slide

  90. Cheers!
    !
    bit.ly/rxjava-coroutines-prague
    @askashdavies

    View Slide