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

Coroutines and Rx: A battle towards Asynchrony

Amanjeet Singh
November 26, 2019
170

Coroutines and Rx: A battle towards Asynchrony

This was given at Droidcon SF 2019

Amanjeet Singh

November 26, 2019
Tweet

Transcript

  1. showsApi.getShowByName("Friends") .flatmap { showsApi.getCharacters(it.id) } .subscribe( { /* do something

    with characters*/ }, { /* handle errors retrieving characters*/} ) -Ben Christensen
  2. I O CoroutineScope.launch(Dispatchers.IO){ val show = showsApi.getShowByName("Friends") val characters =

    showsApi.getCharacters(show.id) withContext(Dispatchers.Main){ // do something with characters on UI } }
  3. I O 0 sm.item = "Friends" sm.label = 1 CoroutineScope.launch(Dispatchers.IO){

    val show = showsApi.getShowByName("Friends") val characters = showsApi.getCharacters(show.id) withContext(Dispatchers.Main){ // do something with characters on UI } }
  4. I O 0 sm.item = "Friends" sm.label = 1 showsApi.getShowsByName(“Friends”,

    cont) Continuation CoroutineScope.launch(Dispatchers.IO){ val show = showsApi.getShowByName("Friends") val characters = showsApi.getCharacters(show.id) withContext(Dispatchers.Main){ // do something with characters on UI } }
  5. I O 0 1 show = sm.result sm.label = 2

    sm.item = "Friends" sm.label = 1 showsApi.getShowsByName(“Friends”, cont) CoroutineScope.launch(Dispatchers.IO){ val show = showsApi.getShowByName("Friends") val characters = showsApi.getCharacters(show.id) withContext(Dispatchers.Main){ // do something with characters on UI } }
  6. I O 0 1 show = sm.result sm.label = 2

    showsApi.getCharacters(show.id, cont) sm.item = "Friends" sm.label = 1 showsApi.getShowsByName(“Friends”, cont) Continuation CoroutineScope.launch(Dispatchers.IO){ val show = showsApi.getShowByName("Friends") val characters = showsApi.getCharacters(show.id) withContext(Dispatchers.Main){ // do something with characters on UI } }
  7. I O 0 1 show = sm.result sm.label = 2

    showsApi.getCharacters(show.id, cont) sm.item = "Friends" sm.label = 1 showsApi.getShowsByName(“Friends”, cont) Continuation 2 characters = sm.result CoroutineScope.launch(Dispatchers.IO){ val show = showsApi.getShowByName("Friends") val characters = showsApi.getCharacters(show.id) withContext(Dispatchers.Main){ // do something with characters on UI } }
  8. Error Propagation over 1 2 3 5 N N /

    N - 4 -1/3 -1 -3 Destructive 4
  9. Error Handling • Wrap your response in Result class sealed

    class Result<out T> { data class Success<out T>(val data: T) : Result<T>() data class Error(val exception: Exception) : Result<Nothing>() } • Gotta catch them all onError onErrorReturn onErrorResumeNext doOnError onErrorComplete onErrorReturnItem
  10. Error Handling fun getMovies(): Observable<Result<Response>> { return moviesApi.getMovies() .map {

    Result.Success(it) } .onErrorReturn { Result.Error(it) } } Result
  11. Error Handling over • Depends on what Coroutine Builder you

    are using: Launch/Async? • Scope Intelligently • Tools for handling ☠ CoroutineExceptionHandler try/catch
  12. Error Handling over private val context: CoroutineContext = Job() +

    Dispatchers.IO val scope = CoroutineScope(context) suspend fun longRunningTask(): Unit { delay(100) throw IllegalArgumentException() } • Setup
  13. Error Handling over • Install CoroutineExceptionHandler private val ceh =

    CoroutineExceptionHandler { _, e -> println("CEH Handled Crash [$e]") } scope.launch(ceh){ launch{ longRunningTask() } }
  14. suspend fun <T : Any> apiCall(call: suspend () -> Result<T>,

    errorMessage: String): Result<T> { return try { call() } catch (e: Exception) { Result.Error(e) } } Let’s be a pro
  15. suspend fun <T : Any> apiCall(call: suspend () -> Result<T>,

    errorMessage: String): Result<T> { return try { call() } catch (e: Exception) { Result.Error(e) } } Result Let’s be a pro
  16. suspend fun getLatestMovies(): Result<MovieResults> { return apiCall( { val data

    = moviesApiInterface.getInTheatreMovies() return@apiCall Result.Success(data.body()) }, "Couldn't get" ) } So, Now are we pro? Let’s be a pro
  17. No Let’s be a pro So, Now are we pro?

    suspend fun getLatestMovies(): Result<MovieResults> { return apiCall( { val data = moviesApiInterface.getInTheatreMovies() return@apiCall Result.Success(data.body()) }, "Couldn't get" ) }
  18. Zip Calls in Observable.zip(getPopularMovies(), getNowPlayingMovies(), BiFunction<Movies, Movies, Movies> { popularMovies,

    topRatedMovies, -> // Filter from popular and top rated movies }.subscribeOn(Scheduler.io())
  19. Zip Calls in Let’s apply TEST rule Observable.zip(getPopularMovies(), getNowPlayingMovies(), BiFunction<Movies,

    Movies, Movies> { popularMovies, topRatedMovies, -> // Filter from popular and top rated movies }.subscribeOn(Scheduler.io())
  20. Zip Calls in Threading Exception Handling Scalability Transforming results Observable.zip(getPopularMovies(),

    getNowPlayingMovies(), BiFunction<Movies, Movies, Movies> { popularMovies, topRatedMovies, -> // Filter from popular and top rated movies }.subscribeOn(Scheduler.io())
  21. Zip Calls in Threading Exception Handling Scalability Transforming results Observable.zip(getPopularMovies(),

    getNowPlayingMovies(), BiFunction<Movies, Movies, Movies> { popularMovies, topRatedMovies, -> // Filter from popular and top rated movies }.subscribeOn(Scheduler.io())
  22. Zip Calls in Threading Exception Handling Scalability Transforming results Observable.zip(getPopularMovies(),

    getNowPlayingMovies(), BiFunction<Movies, Movies, Movies> { popularMovies, topRatedMovies, -> // Filter from popular and top rated movies }.subscribeOn(Scheduler.io())
  23. Zip Calls in Threading Exception Handling Scalability Transforming results Observable.zip(getPopularMovies(),

    getNowPlayingMovies(), BiFunction<Movies, Movies, Movies> { popularMovies, topRatedMovies, -> // Filter from popular and top rated movies }.subscribeOn(Scheduler.io())
  24. suspend fun <T1, T2, R> zip(source1: Deferred<T1>, source2: Deferred<T2>, zipper:

    (T1, T2) -> R) = coroutineScope { // wait for two calls to complete async { zipper(source1.await(), source2.await()) } } Zip Calls in • Let’s cook zip +
  25. suspend fun <T1, T2, R> zip(source1: Deferred<T1>, source2: Deferred<T2>, zipper:

    (T1, T2) -> R) = coroutineScope { // wait for two calls to complete async { zipper(source1.await(), source2.await()) } } Zip Calls in • Let’s cook zip + Zip Deferred 1 Deferred 2 Zipper Result
  26. suspend fun <T1, T2, R> zip(source1: Deferred<T1>, source2: Deferred<T2>, zipper:

    (T1, T2) -> R) = coroutineScope { // wait for two calls to complete async { zipper(source1.await(), source2.await()) } } Zip Calls in • Let’s cook zip +
  27. Zip Calls in coroutineScope.launch { val popularMovies = async{ getPopularMovies()

    } val topRatedMovies = async{ getTopRatedMovies() } zip(popularMovies, topRatedMovies) { popularResults, topRatedResults -> // Filter from popular and top rated movies return filteredResults }.await() } Threading Exception Handling Scalability Transforming results
  28. Threading Exception Handling Scalability Transforming results coroutineScope.launch { val popularMovies

    = async{ getPopularMovies() } val topRatedMovies = async{ getTopRatedMovies() } zip(popularMovies, topRatedMovies) { popularResults, topRatedResults -> // Filter from popular and top rated movies return filteredResults }.await() } Zip Calls in
  29. Threading Exception Handling Scalability Transforming results coroutineScope.launch { val popularMovies

    = async{ getPopularMovies() } val topRatedMovies = async{ getTopRatedMovies() } zip(popularMovies, topRatedMovies) { popularResults, topRatedResults -> // Filter from popular and top rated movies return filteredResults }.await() } Zip Calls in
  30. Threading Exception Handling Scalability Transforming results coroutineScope.launch { val popularMovies

    = async{ getPopularMovies() } val topRatedMovies = async{ getTopRatedMovies() } zip(popularMovies, topRatedMovies) { popularResults, topRatedResults -> // Filter from popular and top rated movies return filteredResults }.await() } Zip Calls in
  31. State Management through Rich with operators, Adheres to R&FP, exceptional

    handling, 
 lifecycle management, etc. val models = events.flatMap { moviesClient.searchMovies(it.searchTerm) } .map { it.results.asResult() } .onErrorReturn { Result.Error(it.toApplicationError()) } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread())
  32. Rich with operators, Adheres to R&FP, exceptional handling, 
 lifecycle

    management, etc. val models = events.flatMap { moviesClient.searchMovies(it.searchTerm) } .map { it.results.asResult() } .onErrorReturn { Result.Error(it.toApplicationError()) } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) State Management through
  33. Rich with operators, Adheres to R&FP, exceptional handling, 
 lifecycle

    management, etc. val models = events.flatMap { moviesClient.searchMovies(it.searchTerm) } .map { it.results.asResult() } .onErrorReturn { Result.Error(it.toApplicationError()) } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) State Management through
  34. Rich with operators, Adheres to R&FP, exceptional handling, 
 lifecycle

    management, etc. val models = events.flatMap { moviesClient.searchMovies(it.searchTerm) } .map { it.results.asResult() } .onErrorReturn { Result.Error(it.toApplicationError()) } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) State Management through
  35. • Suspension based reactive streams. • Can be used to

    represent streams State Management through
  36. • Suspension based reactive streams. • Can be used to

    represent streams State Management through
  37. • Rich with operators, Adheres to R&FP, exceptional handling, 


    lifecycle management, etc. viewModelScope.launch { flowOf(moviesClient.searchMoviesAsync(query).await()) .map { Result.Success(it.results) } .catch { Result.Error(it.toApplicationError()) }.collect { searchStates.postValue(it) } } State Management through
  38. • Rich with operators, Adheres to R&FP, exceptional handling, 


    lifecycle management, etc. viewModelScope.launch { flowOf(moviesClient.searchMoviesAsync(query).await()) .map { Result.Success(it.results) } .catch { Result.Error(it.toApplicationError()) }.collect { searchStates.postValue(it) } } State Management through
  39. • Rich with operators, Adheres to R&FP, exceptional handling, 


    lifecycle management, etc. viewModelScope.launch { flowOf(moviesClient.searchMoviesAsync(query).await()) .map { Result.Success(it.results) } .catch { Result.Error(it.toApplicationError()) }.collect { searchStates.postValue(it) } } State Management through
  40. • Rich with operators, Adheres to R&FP, exceptional handling, 


    lifecycle management, etc. viewModelScope.launch { flowOf(moviesClient.searchMoviesAsync(query).await()) .map { Result.Success(it.results) } .catch { Result.Error(it.toApplicationError()) }.collect { searchStates.postValue(it) } } State Management through
  41. • Rich with operators, Adheres to R&FP, exceptional handling, 


    lifecycle management, etc. viewModelScope.launch { flowOf(moviesClient.searchMoviesAsync(query).await()) .map { Result.Success(it.results) } .catch { Result.Error(it.toApplicationError()) }.collect { searchStates.postValue(it) } } State Management through
  42. • Rich with operators, Adheres to R&FP, exceptional handling, 


    lifecycle management, etc. viewModelScope.launch { flowOf(moviesClient.searchMoviesAsync(query).await()) .map { Result.Success(it.results) } .catch { Result.Error(it.toApplicationError()) }.collect { searchStates.postValue(it) } } UI Bindings? State Management through
  43. References • https://medium.com/the-kotlin-chronicle/coroutine-exceptions-3378f51a7d33 • https://medium.com/@elizarov/ • https://speakerdeck.com/krlrozov/kotlin-coroutines-flow-is-coming • https://speakerdeck.com/ragdroid/flowing-things-not-so-strange-in-the-mvi-world •

    https://github.com/amanjeetsingh150/CoroutinesRx • https://medium.com/capital-one-tech/coroutines-and-rxjava-an-asynchronicity-comparison-part-1-asynchronous- programming-e726a925342a • Kotlin Coroutines Slack Channel • https://github.com/android/plaid