【发布时间】:2020-05-23 19:40:38
【问题描述】:
我一直在通过this codelab 了解协程。我仍然不清楚的一件事是,为什么我们需要更改调度程序以确保我们不会阻塞主/UI 线程?如果协程是轻量级线程,那么当我已经在主线程上时,为什么不能在协程中调用线程阻塞函数(无论它们是否挂起)?
codelab 解释说(概括地说)如果我编写这段代码:
// Repository.kt
suspend fun repoRefreshTitle() {
delay(500)
}
//ViewModel.kt
fun vmRefreshTitle() {
viewModelScope.launch {
_spinner.value = true
repository.repoRefreshTitle()
}
}
...那么这不会阻塞主线程。 delay() 是一个suspend 函数,所以viewmodelScope.launch 创建的协程会暂停,直到500ms 过去。主线程不会被阻塞。
但是,如果我将 repoRefreshTitle() 重构为以下内容:
suspend fun repoRefreshTitle() {
val result = nonSuspendingNetworkCall()
}
...那么该网络调用实际上将在主线程上完成。那是对的吗?我将不得不更改为另一个调度程序以将工作卸载到 IO 线程:
suspend fun repoRefreshTitle() {
withContext(Dispatchers.IO) {
val result = nonSuspendingNetworkCall()
}
}
我一定以某种方式过度简化了这一点。我已经在协程中的事实还不够吗?为什么我必须切换调度程序?
【问题讨论】:
-
“但是,如果我将 refreshTitle() 重构为以下内容”——在您的第一个代码 sn-p 中,您有两个名为
refreshTitle()的函数。为了清楚起见,至少在问题中,如果它们有两个不同的名称会很有帮助。在这种特定情况下,我们不知道您重写了哪一个。我猜它是存储库之一。 -
非常好,感谢您指出这一点。我做了一些调整,以更清楚地区分这 2 个功能。