android - 为什么需要更改协程中的调度程序?

标签 android kotlin-coroutines

我一直在经历 this代码实验室以了解协程。我仍然不清楚的一件事是,为什么我们需要更改调度程序以确保我们不会阻塞主/UI 线程?如果协程是轻量级线程,那么当我已经在主线程上时,为什么我不能在协程中调用线程阻塞函数(无论它们是否挂起)?

代码实验室解释说(总结)如果我写这段代码:

// Repository.kt
suspend fun repoRefreshTitle() {
    delay(500)
}

//ViewModel.kt
fun vmRefreshTitle() {
   viewModelScope.launch {
       _spinner.value = true
       repository.repoRefreshTitle()
   }
}

...那么这不会阻塞主线程。 delay() 是一个suspend 函数,因此由viewmodelScope.launch 创建的协程将暂停,直到 500 毫秒过去。但是主线程不会被阻塞。

但是,如果我将 repoRefreshTitle() 重构为以下内容:

suspend fun repoRefreshTitle() {
    val result = nonSuspendingNetworkCall()
}

...那么该网络调用实际上将在主线程上完成。那是对的吗?我将不得不更改为另一个调度程序以将工作卸载到 IO 线程:

suspend fun repoRefreshTitle() {
    withContext(Dispatchers.IO) {
        val result = nonSuspendingNetworkCall()
    }
}

我一定是在某种程度上过度简化了这一点。难道我已经在协程中就够了吗?为什么我必须切换调度程序?

最佳答案

The codelab explains that (in summary) if I write this code...then this won't block the main thread. delay() is a suspend function, so the coroutine created by viewmodelScope.launch will be paused until the 500ms passes. The main thread won't be blocked though.

正确。但是,delay() 中几乎没有真正的“工作”将在主应用程序线程上执行,因为 viewModelScope.launch() 的默认调度程序是基于Dispatchers.Main

However, if I refactor repoRefreshTitle() to the following...then that network call will actually be done on the main thread. Is that correct?

正确。 nonSuspendingNetworkCall()delay() 一样,将在主应用程序线程上运行。在 nonSuspendingNetworkCall() 中,这不是一件好事。

I would have to change to another dispatcher to offload the work to an IO thread

正确。更具体地说,您需要使用一个使用后台线程的调度程序。对于 I/O,Dispatchers.IO 是一个常见的选择。

Isn't the fact that I'm already in a coroutine enough? Why do I have to switch the dispatcher?

因为我们不想在主应用程序线程上进行网络 I/O。 Dispatchers.Main 在主应用程序线程上运行其协程,这是 viewModelScope.launch() 的默认调度程序。这就是为什么在我写的很多东西中,我专门编写 viewModelScope.launch(Dispatchers.Main) 的原因之一——这更冗长(并且在技术上与默认设置略有不同),但它是对读者来说更明显。

关于android - 为什么需要更改协程中的调度程序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61977672/

相关文章:

Kotlin 协程 GlobalScope.launch 与 runBlocking

Android Livedata Observer 协程 Kotlin

android - 外部链接无法打开 phonegap cordova android

android - 找不到 fragment.jar (androidx.fragment :fragment:1. 0.0)

android - Firebase ML-Kit 并行运行多个人脸检测 session

kotlin - 如何在 kotlin 中创建这个协程?

android - 如果使用标准 Fragment,LiveData 不会调用 LifecycleRegistryOwner

javascript - 如何调整 Javascript 元素的大小以适应其容器窗口

java - 当我使用改造时,由 : java. lang.NoSuchFieldError : com. squareup.okhttp.internal.http.HttpMethod.METHODS 引起

android - 如何使用 Kotlin Flow 从异步回调中发出数据?