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

Android Coroutine Recipes

Android Coroutine Recipes

Presentation Source: https://goo.gl/fTP5or

Brief introduction to Kotlin coroutines with practical android samples.

Dmytro Danylyk

March 20, 2018
Tweet

More Decks by Dmytro Danylyk

Other Decks in Technology

Transcript

  1. object MyTask: AsyncTask() { override fun doInBackground { code }

    override fun onPostExecute { code } } Thread { code } .start() What we have
  2. object MyTask: AsyncTask() { override fun doInBackground { code }

    override fun onPostExecute { code } } Thread { code } .start() What we have
  3. object MyTask: AsyncTask() { override fun doInBackground { code }

    override fun onPostExecute { code } } Observable.just(1,2,3,4) .map { code } .doOnEach { code } .doOnError { code } .subscribe( { onNext }, { onError } ) Thread { code } .start() What we have
  4. object MyTask: AsyncTask() { override fun doInBackground { code }

    override fun onPostExecute { code } } Observable.just(1,2,3,4) .map { code } .doOnEach { code } .doOnError { code } .subscribe( { onNext }, { onError } ) Thread { code } .start() What we have
  5. object MyTask: AsyncTask() { override fun doInBackground { code }

    override fun onPostExecute { code } } Observable.just(1,2,3,4) .map { code } .doOnEach { code } .doOnError { code } .subscribe( { onNext }, { onError } ) Thread { code } .start() What we have
  6. What we want fun loadData() = launch { view.showLoading() val

    result = async { // computation }.await() view.showData(result) view.hideLoading() }
  7. launch() Job fire and forget uncaught exception crash app withContext()

    T returns result uncaught exception crash app
  8. launch() async() Job Deferred fire and forget returns non-blocking future

    uncaught exception crash app withContext() T returns result uncaught exception crash app uncaught exception is stored inside the resulting Deferred
  9. CoroutineContext UI Unconfined CommonPool dispatch execution into background thread dispatch

    execution into Android main thread dispatch execution into current thread
  10. How to launch coroutine // kotlinx.coroutines.experimental.android val uiContext: CoroutineContext =

    UI // kotlinx.coroutines.experimental val bgContext: CoroutineContext = CommonPool
  11. How to launch coroutine private fun loadData() { view.showLoading() val

    result = dataProvider.provideData() view.showData(result) view.hideLoading() }
  12. How to launch coroutine private fun loadData() = launch(uiContext) {

    view.showLoading() val result = dataProvider.provideData() view.showData(result) view.hideLoading() } parent coroutine
  13. How to launch coroutine private fun loadData() = launch(uiContext) {

    view.showLoading() val result = withContext(bgContext) { dataProvider.provideData() } view.showData(result) view.hideLoading() } child coroutine
  14. How to launch coroutine private fun loadData() = launch(uiContext) {

    view.showLoading() // ui thread val result = withContext(bgContext) { dataProvider.provideData() } // bg view.showData(result) // ui thread view.hideLoading() }
  15. Launch two tasks sequentially private fun loadData() = launch(uiContext) {

    view.showLoading() // ui thread // non ui thread, suspend until task is finished val result1 = withContext(bgContext) { dataProvider.provideData() } // non ui thread, suspend until task is finished val result2 = withContext(bgContext) { dataProvider.provideData() } val result = "$result1 $result2" // ui thread view.showData(result) view.hideLoading() }
  16. Launch two tasks sequentially private fun loadData() = launch(uiContext) {

    view.showLoading() // ui thread // non ui thread, suspend until task is finished val result1 = withContext(bgContext) { dataProvider.provideData() } // non ui thread, suspend until task is finished val result2 = withContext(bgContext) { dataProvider.provideData() } val result = "$result1 $result2" // ui thread view.showData(result) view.hideLoading() }
  17. Launch two tasks in parallel private fun loadData() = launch(uiContext)

    { view.showLoading() // ui thread val task1 = async(bgContext) { dataProvider.provideData() } val task2 = async(bgContext) { dataProvider.provideData() } // non ui thread, suspend until finished val result = "${task1.await()} ${task2.await()}" view.showData(result) // ui thread view.hideLoading() }
  18. Launch two tasks in parallel private fun loadData() = launch(uiContext)

    { view.showLoading() // ui thread val task1 = async(bgContext) { dataProvider.provideData() } val task2 = async(bgContext) { dataProvider.provideData() } // non ui thread, suspend until finished val result = "${task1.await()} ${task2.await()}" view.showData(result) // ui thread view.hideLoading() }
  19. Launch a coroutine with a timeout private fun loadData() =

    launch(uiContext) { view.showLoading() // ui thread // non ui thread, suspend until the task is finished // or return null in 2 sec val result = withTimeoutOrNull(2, TimeUnit.SECONDS) { withContext(bgContext) { dataProvider.provideData() } } view.showData(result) // ui thread view.hideLoading() }
  20. Launch a coroutine with a timeout private fun loadData() =

    launch(uiContext) { view.showLoading() // ui thread // non ui thread, suspend until the task is finished // or return null in 2 sec val result = withTimeoutOrNull(2, TimeUnit.SECONDS) { withContext(bgContext) { dataProvider.provideData() } } view.showData(result) // ui thread view.hideLoading() }
  21. Сancel a coroutine private var job: Job? = null fun

    onNextClicked() { job = loadData() } fun onBackClicked() { job?.cancel() } private fun loadData() = launch(uiContext) { // code }
  22. Сancel a coroutine private var job: Job? = null fun

    onNextClicked() { job = loadData() } fun onBackClicked() { job?.cancel() } private fun loadData() = launch(uiContext) { // code }
  23. Сancel a coroutine private var job: Job? = null fun

    onNextClicked() { job = loadData() } fun onBackClicked() { job?.cancel() } private fun loadData() = launch(uiContext) { // code }
  24. try-catch block private fun loadData() = launch(uiContext) { view.showLoading() //

    ui thread try { val result = withContext(bgContext) { dataProvider.provideData() } view.showData(result) // ui thread } catch (e: IllegalArgumentException) { e.printStackTrace() } view.hideLoading() }
  25. Store inside Deferred private fun loadData() = async(uiContext) { view.showLoading()

    // ui thread val task = async(bgContext) { dataProvider.provideData() } val result = task.await() // non ui thread view.showData(result) // ui thread view.hideLoading() }
  26. Exception handler val exceptionHandler= CoroutineExceptionHandler { _, throwable -> throwable.printStackTrace()

    } private fun loadData() = launch(uiContext + exceptionHandler) { // code }
  27. Return null if exception was thrown /** * return T

    or null if exception was thrown */ suspend fun <T> Deferred<T>.awaitSafe(): T? = try { await() } catch (e: Exception) { e.printStackTrace() null }
  28. Return null if exception was thrown private fun loadData() =

    launch(uiContext) { val task = async(bgContext) { dataProvider.provideData() } val result = task.awaitSafe() if(result != null) { // success } else { // failure } }
  29. Return Result<T> /** * return Result<T> which contain either success

    or failure */ suspend fun <T> Deferred<T>.awaitResult(): Result<T> = try { Result(success = await(), failure = null) } catch (e: Exception) { Result(success = null, failure = e) } data class Result<out T>(val success: T?, val failure: Exception?)
  30. Return Result<T> private fun loadData() = launch(uiContext) { val task

    = async(bgContext) { dataProvider.provideData() } val (success, failure) = task.awaitResult() if(success != null) { // success T } else { // failure Exception } }
  31. Testing: Unconfined context class MainPresenter(val uiContext: CoroutineContext = UI, val

    bgContext: CoroutineContext = CommonPool) { fun loadData() = launch(uiContext) { ... } }
  32. Testing: Unconfined context @Test fun test() { val presenter =

    MainPresenter(Unconfined, Unconfined) // test presenter.loadData() // verify verify(mockView).showLoading() verify(mockDataProvider).provideData() }
  33. Callback to coroutine fun loadData() { val docRef = db.collection("cities").document("NSW")

    docRef.get().addOnCompleteListener(OnCompleteListener { task -> if (task.isSuccessful) { // task.result.data } else { // task.exception } }) }
  34. Callback to coroutine suspend fun loadData() = suspendCoroutine<Snapshot> { continuation

    -> val docRef = db.collection("cities").document("NSW") docRef.get().addOnCompleteListener(OnCompleteListener { task -> if (task.isSuccessful) { // task.result.data } else { // task.exception } }) }
  35. Callback to coroutine suspend fun loadData() = suspendCoroutine<Snapshot> { continuation

    -> val docRef = db.collection("cities").document("NSW") docRef.get().addOnCompleteListener(OnCompleteListener { task -> if (task.isSuccessful) { continuation.resume(task.result) } else { continuation.resumeWithException(task.exception) } }) }
  36. Callback to coroutine suspend fun loadData() = suspendCoroutine<Snapshot> { continuation

    -> val docRef = db.collection("cities").document("NSW") docRef.get().addOnCompleteListener(OnCompleteListener { task -> if (task.isSuccessful) { continuation.resume(task.result) } else { continuation.resumeWithException(task.exception) } }) }
  37. Coroutine modules // reactive implementation 'kotlinx-coroutines-reactive' implementation 'kotlinx-coroutines-reactor' implementation 'kotlinx-coroutines-rx1'

    implementation 'kotlinx-coroutines-rx2' // integration implementation 'kotlinx-coroutines-guava' implementation 'kotlinx-coroutines-jdk8' implementation 'kotlinx-coroutines-nio' implementation 'kotlinx-coroutines-quasar'
  38. Coroutine modules // retrofit 2 interface MyService { @GET("/user") fun

    getUser(): Deferred<User> // or @GET("/user") fun getUser(): Deferred<Response<User>> }
  39. Resources www.github.com/kotlin/kotlinx.coroutines /ui /reactive /integration - documentation - guide to

    UI programming - guide to reactive streams - guide to future-based libraries