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

Droidcon Lisbon: RxJava & Coroutines: A Practical Analysis

Ash Davies
September 10, 2019

Droidcon Lisbon: RxJava & Coroutines: A Practical Analysis

Kotlin has taken the Android world by storm, and is quickly becoming the most popular language of choice. 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. RxJava has already proven its stability and usefulness, and a comparison between the two frameworks shows that they are simply better suited for different purposes.

In this talk you’ll learn:
- how to 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

September 10, 2019
Tweet

More Decks by Ash Davies

Other Decks in Programming

Transcript

  1. Coroutine Builders val deferred: Deferred<String> = async { "Hello World!"

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

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

    } val result: String = deferred.await() val job: Job = launch { "Hello World!" } job.join() @askashdavies
  4. Dispatchers » Default » IO » Main » Android (Main

    Thread Dispatcher) » JavaFx (Application Thread Dispatcher) » Swing (Event Dispatcher Thread) » Unconfined @askashdavies
  5. ! 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
  6. "If all you have is a hammer, everything looks like

    a nail" — Abraham Maslow, The Psychology of Science, 1966 @askashdavies
  7. Network Call Handling // RxJava2: Single<T> fun getUser(): Single<User> =

    Single.fromCallable { /* ... */ } // Coroutines: T suspend fun getUser(): User = /* ... */ @askashdavies
  8. Cache Retrieval // RxJava2: Maybe<T> fun getUser(): Maybe<User> = Maybe.fromCallable

    { /* ... */ } // Coroutines: T? suspend fun getUser(): User? = /* ... */ @askashdavies
  9. Background Task Handling // RxJava2: Completable fun storeUser(user: User): Completable.fromCallable

    { /* ... */ } // Coroutines: Unit suspend fun storeUser(user: User) { /* ... */ } @askashdavies
  10. 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
  11. ! FlatMap // RxJava2 getUser() .flatMap { doSomethingWithUser(it) } .subscribeOn(Schedulers.computation())

    .observeOn(AndroidSchedulers.mainThread()) .subscribe { /* Do something else */ } // Coroutines launch(Dispatchers.Main) { val user = getUser() val smth = doSomethingWithUser(user) /* Do something else */ } @askashdavies
  12. Callback Consumption // RxJava2 fun getSingle(): Single = Single.create<T> {

    emitter -> doSomethingAsync(object: Callback<T> { override fun onComplete(result: T) = emitter.onSuccess(result) override fun onException(exception: Exception) = emitter.onError(exception) }) } // Coroutines suspend fun getCoroutine() : T = suspendCoroutine { continuation -> doSomethingAsync(object : Callback<T> { override fun onComplete(result: T) = continuation.resume(result) override fun onException(exception: Exception) = continuation.resumeWithException(exception) }) } @askashdavies
  13. 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
  14. Roman Elizarov Cold flows, hot channels flow { emit("Hello") emit("World")

    } medium.com/@elizarov/cold-flows-hot-channels-d74769805f9
  15. Flow ! flow { emit("Hello world") } .onEach { delay(500)

    } .map { it.length } .collect { println("Length: $it") } @askashdavies
  16. Testing @Test fun testFoo() = runBlockingTest { val actual =

    foo() // ... } suspend fun foo() { delay(1_000) // ... } github.com/Kotlin/kotlinx.coroutines/tree/master/kotlinx-coroutines-test
  17. runBlockingTest » Auto-advancing of time ⏱⏩ » Explicit time control

    # » Eager execution of launch or async » Pause, manually advance, and restart execution » Report uncaught exceptions as test failures @askashdavies
  18. "If it ain't broke, don't fix it" — Bert Lance,

    Nation's Business, 1977 @askashdavies
  19. interface UserService { @GET("/user") suspend fun getUser(): User } GlobalScope.launch

    { val user = retrofit .create<UserService>() // >= 2.5.0 .getUser() } @askashdavies
  20. 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
  21. 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
  22. Maybe.await() GlobalScope.launch { val result: String? = Maybe .fromCallable {

    null as String? } .await() // result == null } @askashdavies
  23. 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