为什么viewModelScope.launch默认运行在主线程上

Jon*_*gat 18 multithreading android coroutine android-architecture-components

在我学习协程以及如何在 android 应用程序中正确使用它们时,我发现了一些令我感到惊讶的事情。

在启动viewModelScope.launch { }lambda 中使用并设置断点启动协程时,我注意到我的应用程序不再响应,因为它仍在主线程上。

这让我感到困惑,因为文档viewModelScope.launch { }明确指出:

在不阻塞当前线程的情况下启动一个新的协程

当前线程不是主线程吗?如果默认情况下它不在不同的线程上运行,那么启动的全部目的是什么?

我能够viewModelScope.launch(Dispatchers.IO){ }在另一个线程上使用它按我的预期工作,即在另一个线程上运行它。

我试图通过该launch方法完成的是调用存储库并执行一些 IO 工作,即调用 Web 服务并将数据存储在房间数据库中。所以我想调用viewModelScope.launch(Dispatchers.IO){ }在不同的线程上完成所有工作,最后更新 LiveData 结果。

viewModelScope.launch(Dispatchers.IO){ liveData.postValue(someRepository.someWork()) }

所以我的第二个问题是,这是要走的路吗?

ngo*_*goa 12

ViewModelScope.launch { } 在主线程上运行,但也为您提供了运行其他调度程序的选项,因此您可以同步运行 UI 和后台操作。

以你为例:

fun thisWillRunOnMainThread() {

    viewModelScope.launch { 

        //below code will run on UI thread.
        showLoadingOnUI()

        //using withContext() you can run a block of code on different dispatcher
        val result = novel.id = withContext(Dispatchers.IO) {
            withsomeRepository.someWork()
        }

        //The below code waits until the above block is executed and the result is set.
        liveData.value = result
        finishLoadingOnUI()
    }
}
Run Code Online (Sandbox Code Playgroud)

为了获得更多参考,我想说有一些简洁的文章可以帮助您理解这个概念。

中等链接,解释它非常简洁。


Epi*_*rce 7

所以我的第二个问题是,这是要走的路吗?

我希望您目前的方法有两件事会有所不同。

1.) 第一步是通过 定义后台操作的调度程序withContext

class SomeRepository {
    suspend fun doWork(): SomeResult = withContext(Dispatchers.IO) {
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

这样,操作本身在后台线程上运行,但您没有强制您的原始范围为“线程外”。

2.) Jetpack Lifecycle KTX 提供了liveData {协程构建器,因此您不必postValue手动进行。

val liveData: LiveData<SomeResult> = liveData {
    emit(someRepository.someWork())
}
Run Code Online (Sandbox Code Playgroud)

现在你甚至不用到后顾之忧viewModelScopelaunch等。


mur*_*ish 5

默认主线程背后的想法是您可以运行 UI 操作而无需更改上下文。我猜这是 Kotlin 协程库作者选择的约定

假设默认情况下启动在 IO 线程上运行,那么代码将如下所示

viewmodelScope.launch {
  val response = networkRequest() 
  withContext(Dispatchers.Main) {
    renderUI(response)
  } 
}
Run Code Online (Sandbox Code Playgroud)

假设如果默认情况下启动在默认线程上运行,那么代码将如下所示

viewmodelScope.launch {
  val response: Response = null
  withContext(Dispatchers.IO) {
     response = networkRequest()
  }
  withContext(Dispatchers.Main) {
    renderUI(response)
  } 
}
Run Code Online (Sandbox Code Playgroud)

由于默认启动是在主线程上,所以现在您必须执行以下操作

viewmodelScope.launch {
  val response: Response = null
  withContext(Dispatchers.IO) {
     response = networkRequest()
  }
  renderUI(response)
}
Run Code Online (Sandbox Code Playgroud)

为了避免使用 null 初始化响应的混乱代码,我们还可以将 networkRequest 设为挂起,并将 networkRequest() 函数的业务逻辑包装在 withContext(Dispatchers.IO) 中,这也是很多人编写 networkRequest() 函数的方式!希望这是有道理的