I've been going through this codelab to learn about coroutines. One thing that still isn't clear to me is that why do we need to change dispatchers to ensure that we don't block the main/UI thread? If coroutines are light-weight threads, then why can't I invoke thread-blocking functions (whether they're suspending or not) within a coroutine when I'm already on the main thread?
如果我编写以下代码,则代码实验室对此进行了总结:
// Repository.kt
suspend fun repoRefreshTitle() {
delay(500)
}
//ViewModel.kt
fun vmRefreshTitle() {
viewModelScope.launch {
_spinner.value = true
repository.repoRefreshTitle()
}
}
...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.
However, if I refactor repoRefreshTitle()
to the following:
suspend fun repoRefreshTitle() {
val result = nonSuspendingNetworkCall()
}
...然后网络调用实际上将在主线程上完成。那是对的吗?我将不得不更改为另一个调度程序,以将工作分流到IO线程:
suspend fun repoRefreshTitle() {
withContext(Dispatchers.IO) {
val result = nonSuspendingNetworkCall()
}
}
我必须以某种方式过分简化。我已经足够接受协程了吗?为什么必须切换调度程序?
When you run your code inside
viewModelScope
it doesn't mean your main thread won't freeze. It just ensures if you started work on MainThread and you are waiting for another thread to return a result, it won't block the main thread, e.g. calling an API with Retrofit and waiting to update LiveData in your ViewModel.So Why do you need to change Coroutine Scope? (probably using
withContext
)您开始在主线程上工作,然后切换到另一个协程进行繁重的工作,并轻松地将结果返回到主线程上。
Correct. However, what little real "work" there is in
delay()
will be performed on the main application thread, because the default dispatcher forviewModelScope.launch()
is based onDispatchers.Main
.Correct.
nonSuspendingNetworkCall()
, likedelay()
, will be run on the main application thread. In thenonSuspendingNetworkCall()
, that's not a good thing.Correct. More specifically, you need to use a dispatcher that uses a background thread. For I/O,
Dispatchers.IO
is a common choice.Because we do not want to do network I/O on the main application thread.
Dispatchers.Main
runs its coroutines on the main application thread, and that is the default dispatcher forviewModelScope.launch()
. That's one of the reasons why, in a lot of what I write, I specifically writeviewModelScope.launch(Dispatchers.Main)
— that's more wordy (and technically slightly different than the default), but it is more obvious to readers.