让我们首先从源代码中剖析viewModelScope。它使用CouroutineScope 的自定义实现。上下文由SupervisorJob 和Dispatchers.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!