Kotlin Coroutines the right way in Android

前端 未结 9 995
醉酒成梦
醉酒成梦 2020-12-02 06:51

I\'m trying to update a list inside the adapter using async, I can see there is too much boilerplate.

Is it the right way to use Kotlin Coroutines?

can this

相关标签:
9条回答
  • 2020-12-02 07:08

    All the above answers are right, but I was having a hard time finding the right import for the UI from kotlinx.coroutines, it was conflicting with UI from Anko. Its

    import kotlinx.coroutines.experimental.android.UI
    
    0 讨论(0)
  • 2020-12-02 07:14

    How to launch a coroutine

    In the kotlinx.coroutines library you can start new coroutine using either launch or async function.

    Conceptually, async is just like launch. It starts a separate coroutine which is a light-weight thread that works concurrently with all the other coroutines.

    The difference is that launch returns a Job and does not carry any resulting value, while async returns a Deferred - a light-weight non-blocking future that represents a promise to provide a result later. You can use .await() on a deferred value to get its eventual result, but Deferred is also a Job, so you can cancel it if needed.

    Coroutine context

    In Android we usually use two context:

    • uiContext to dispatch execution onto the Android main UI thread (for the parent coroutine).
    • bgContext to dispatch execution in background thread (for the child coroutines).

    Example

    //dispatches execution onto the Android main UI thread
    private val uiContext: CoroutineContext = UI
    
    //represents a common pool of shared threads as the coroutine dispatcher
    private val bgContext: CoroutineContext = CommonPool
    

    In following example we are going to use CommonPool for bgContext which limit the number of threads running in parallel to the value of Runtime.getRuntime.availableProcessors()-1. So if the coroutine task is scheduled, but all cores are occupied, it will be queued.

    You may want to consider using newFixedThreadPoolContext or your own implementation of cached thread pool.

    launch + async (execute task)

    private fun loadData() = launch(uiContext) {
        view.showLoading() // ui thread
    
        val task = async(bgContext) { dataProvider.loadData("Task") }
        val result = task.await() // non ui thread, suspend until finished
    
        view.showData(result) // ui thread
    }
    

    launch + async + async (execute two tasks sequentially)

    Note: task1 and task2 are executed sequentially.

    private fun loadData() = launch(uiContext) {
        view.showLoading() // ui thread
    
        // non ui thread, suspend until task is finished
        val result1 = async(bgContext) { dataProvider.loadData("Task 1") }.await()
    
        // non ui thread, suspend until task is finished
        val result2 = async(bgContext) { dataProvider.loadData("Task 2") }.await()
    
        val result = "$result1 $result2" // ui thread
    
        view.showData(result) // ui thread
    }
    

    launch + async + async (execute two tasks parallel)

    Note: task1 and task2 are executed in parallel.

    private fun loadData() = launch(uiContext) {
        view.showLoading() // ui thread
    
        val task1 = async(bgContext) { dataProvider.loadData("Task 1") }
        val task2 = async(bgContext) { dataProvider.loadData("Task 2") }
    
        val result = "${task1.await()} ${task2.await()}" // non ui thread, suspend until finished
    
        view.showData(result) // ui thread
    }
    

    How to cancel a coroutine

    The function loadData returns a Job object which may be cancelled. When the parent coroutine is cancelled, all its children are recursively cancelled, too.

    If the stopPresenting function was called while dataProvider.loadData was still in progress, the function view.showData will never be called.

    var job: Job? = null
    
    fun startPresenting() {
        job = loadData()
    }
    
    fun stopPresenting() {
        job?.cancel()
    }
    
    private fun loadData() = launch(uiContext) {
        view.showLoading() // ui thread
    
        val task = async(bgContext) { dataProvider.loadData("Task") }
        val result = task.await() // non ui thread, suspend until finished
    
        view.showData(result) // ui thread
    }
    

    The complete answer is available in my article Android Coroutine Recipes

    0 讨论(0)
  • 2020-12-02 07:18

    Here's the right way to use Kotlin Coroutines. Coroutine scope simply suspends the current coroutine until all child coroutines have finished their execution. This example explicitly shows us how child coroutine works within parent coroutine.

    An example with explanations:

    fun main() = blockingMethod {                    // coroutine scope         
    
        launch { 
            delay(2000L)                             // suspends the current coroutine for 2 seconds
            println("Tasks from some blockingMethod")
        }
    
        coroutineScope {                             // creates a new coroutine scope 
    
            launch {
                delay(3000L)                         // suspends this coroutine for 3 seconds
                println("Task from nested launch")
            }
    
            delay(1000L)
            println("Task from coroutine scope")     // this line will be printed before nested launch
        } 
    
        println("Coroutine scope is over")           // but this line isn't printed until nested launch completes
    }
    

    Hope this helps.

    0 讨论(0)
提交回复
热议问题