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?
The codelab explains that (in summary) if I write this code:
// 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()
}
...then that network call will actually be done on the main thread. Is that correct? I would have to change to another dispatcher to offload the work to an IO thread:
suspend fun repoRefreshTitle() {
withContext(Dispatchers.IO) {
val result = nonSuspendingNetworkCall()
}
}
I must be oversimplifying this somehow. Isn't the fact that I'm already in a coroutine enough? Why do I have to switch the dispatcher?