什么时候使用 withContext?

bal*_*ekg 6 android coroutine kotlin android-viewmodel kotlin-coroutines

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

Sid*_*ria 9

让我们viewModelScope从它的源代码中剖析第一个。它使用CouroutineScope. 上下文包括一个SupervisorJob和一个Dispatchers.Main调度程序。这确保协程在主线程上启动,并且它的失败不会影响范围内的其他协程。

CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate))
Run Code Online (Sandbox Code Playgroud)

几个值得探索的例子。

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
Run Code Online (Sandbox Code Playgroud)

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

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

总之,viewModelScope将使用主线程执行一个协程,该协程的取消不会影响其他协程。withContext当您想以挂起方式从主线程执行繁重任务时使用;使用适当的调度程序调度它。Kotlin 协程指南值得一读。

编辑:

将以下代码视为单个执行单元。这说明了这样一个事实,即当使用 时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!
Run Code Online (Sandbox Code Playgroud)