【问题标题】:When to use withContext?何时使用 withContext?
【发布时间】:2020-09-25 08:08:31
【问题描述】:

目前我的代码看起来像这样,我有一个 ViewModel,它调用存储库进行一些后台计算并返回结果。
ViewModel 函数使用viewModelScope.launch(Dispatchers.IO) 运行,然后存储库一个是suspend 函数。
我是否必须使用 return withContext{} 来确保一切都将按顺序完成?我查了一下,确实是顺序的,但是在文档中我发现它不一定是?

【问题讨论】:

    标签: android kotlin kotlin-coroutines coroutine android-viewmodel


    【解决方案1】:

    让我们首先从源代码中剖析viewModelScope。它使用CouroutineScope 的自定义实现。上下文由SupervisorJobDispatchers.Main 调度程序组成。这样可以确保协程在主线程上启动,并且它的失败不会影响范围内的其他协程。

    CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate))
    

    几个值得探索的例子。

    viewModelScope.launch {
        Log.d("ViewModel", "Just viewModelScope: ${Thread.currentThread().name}")
    }
    // Output: Just viewModelScope: main
    
    viewModelScope.launch(Dispatchers.IO) {
        Log.d("ViewModel", "IO viewModelScope: ${Thread.currentThread().name}")
    }
    // Output: IO viewModelScope: DefaultDispatcher-worker-3
    
    viewModelScope.launch {
        Log.d("ViewModel", "viewModelScope thread: ${Thread.currentThread().name}")
        withContext(Dispatchers.IO) {
            delay(3000)
            Log.d("ViewModel", "withContext thread: ${Thread.currentThread().name}")
        }
        Log.d("ViewModel", "I'm finished!")
    }
    // Output: 
    // viewModelScope thread: main
    // withContext thread: DefaultDispatcher-worker-4
    

    我检查过,它确实是连续的,但在文档中我 发现不一定是这样。

    withContext() 是一个挂起操作,协程将挂起直到完成,然后继续。从上面的第三个示例中可以明显看出这一点。

    综上所述,viewModelScope 将使用主线程执行一个协程,其取消不会影响其他协程。当您想以挂起的方式在主线程之外执行繁重的任务时,请使用withContext;使用适当的调度程序调度它。 Kotlin Coroutine guide 值得一读。

    编辑:

    将以下代码视为单个执行单元。这说明了当使用withContext() 时,调用者线程处于挂起状态,但它没有被阻塞,这使得它可以继续处理一些其他待处理的工作。输出记录器的交错是我们感兴趣的。

    viewModelScope.launch {
        Log.d("ViewModel", "viewModelScope thread: ${Thread.currentThread().name}")
        withContext(Dispatchers.IO) {
            delay(3000)
            Log.d("ViewModel", "withContext thread: ${Thread.currentThread().name}")
        }
        Log.d("ViewModel", "I'm finished!")
    }
    
    viewModelScope.launch {
        Log.d("ViewModel", "I'm not blocked: ${Thread.currentThread().name}")
    }
    
    // Output: 
    // viewModelScope thread: main
    // I'm not blocked: main
    // withContext thread: DefaultDispatcher-worker-2
    // I'm finished!
    

    【讨论】:

    • 重点是不使用withContext是sequental的,只是配合viewmodelscope.launch执行挂起函数
    • 您是在暗示使用withContext或不使用withContext是连续的吗?
    • 如果是这样的话;挂起函数的强大之处在于,当您使用withContext 时,执行将被卸载到不同的线程,而当另一个线程正在工作时,调用线程可以去完成一些其他工作。
    • 好的,非常感谢。现在我明白我的想法是错误的,当你想要协程的结果时,你需要 withContext,而不是在我的情况下的协程中。
    • 几点澄清 - withContext 使其中的代码在执行转移到另一个线程时暂停,它不是协程构建器。协程构建器是launchasync。如果您想从协程异步返回结果,您可以探索async / await 而不是launch
    猜你喜欢
    • 2020-10-22
    • 1970-01-01
    • 2021-01-07
    • 1970-01-01
    • 1970-01-01
    • 2023-01-16
    • 1970-01-01
    • 2017-07-24
    • 2020-10-08
    相关资源
    最近更新 更多