/coroutine-recipes

Android Coroutine Recipes

Primary LanguageKotlin

Learn Kotlin Coroutines for Android by example

Article: Android Coroutine Recipes

Slides: Android Coroutine Recipes

Contains following examples:

  • How to launch a coroutine
  • How to launch coroutine with a timeout
  • How to launch coroutine which perform 2 background tasks sequentially
  • How to launch coroutine which perform 2 background tasks in parallel
  • How to cancel a coroutine
  • How to catch exception thrown inside coroutine (try/catch)
  • How to catch exception thrown inside coroutine (exception handler)
  • Lifecycle Aware Coroutine - Lifecycle Observer
  • Lifecycle Aware Coroutine - Scoped Fragment

How to launch a coroutine

fun loadData() = GlobalScope.launch(uiContext + job) {
    showLoading() // ui thread

    val result = dataProvider.loadData() // non ui thread, suspend until finished

    showText(result) // ui thread
    hideLoading() // ui thread
}

You can get full code here

How to launch coroutine with a timeout

fun loadData() = GlobalScope.launch(uiContext + job) {
    showLoading()

    val result = withTimeoutOrNull(1, TimeUnit.SECONDS) { dataProvider.loadData() }

    showText(result ?: "Timeout")
    hideLoading()
}

You can get full code here

How to launch coroutine which perform 2 background tasks sequentially

fun loadData() = GlobalScope.launch(uiContext + job) {
    showLoading()

    val result1 = dataProvider.loadData()
    val result2 = dataProvider.loadData()

    showText("$result1\n$result2")
    hideLoading()
}

You can get full code here

How to launch coroutine which perform 2 background tasks in parallel

fun loadData() = GlobalScope.launch(uiContext + job) {
    showLoading()

    val result1 = async { dataProvider.loadData() }
    val result2 = async { dataProvider.loadData() }

    val data = "${result1.await()}\n${result2.await()}"
    showText(data)
    hideLoading()
}

You can get full code here

How to cancel a coroutine

private var job: Job = Job()

fun loadData() = GlobalScope.launch(uiContext + job) {
    ...
}

override fun onDestroyView() {
    super.onDestroyView()
    job.cancel()
}

You can get full code here

How to catch exception thrown inside coroutine (try/catch) source

fun loadData() = GlobalScope.launch(uiContext + job) {
    showLoading()

    try {
        val result = dataProvider.loadData()
        showText(result)
    } catch (e: IllegalArgumentException) {
        showText(e.message ?: "")
    }

    hideLoading()
}

You can get full code here

How to catch exception thrown inside coroutine (exception handler)

val exceptionHandler: CoroutineContext = CoroutineExceptionHandler { _, throwable ->
    showText(throwable.message ?: "")
    hideLoading()
    job = Job() // exception handler cancels job
}

// we can attach CoroutineExceptionHandler to parent context
fun loadData() = GlobalScope.launch(uiContext + job + exceptionHandler) {
    ...
}

You can get full code here

Lifecycle Aware Coroutine - Lifecycle Observer

You can get full code here

class MainScope : CoroutineScope, LifecycleObserver {

    private lateinit var job: Job
    override val coroutineContext: CoroutineContext
        get() = job + Dispatchers.Main

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun onCreate() {
        job = Job()
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun destroy() = job.cancel()
}

class MyFragment : ScopedFragment() {

    private val mainScope = MainScope()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        lifecycle.addObserver(mainScope)
    }

    private fun loadData() = mainScope.launch {
        ...
    }
}

Lifecycle Aware Coroutine - Scoped Fragment

You can get full code here

abstract class ScopedFragment : Fragment(), CoroutineScope {

    protected lateinit var job: Job
    override val coroutineContext: CoroutineContext
        get() = job + Dispatchers.Main

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        job = Job()
    }

    override fun onDestroy() {
        super.onDestroy()
        job.cancel()
    }
}

class MyFragment : ScopedFragment() {

    private fun loadData() = launch {
        ...
    }
}