viewModelScope.launch 不会阻塞 main UI 吗?

Will not viewModelScope.launch block main UI?

代码A使用了viewModelScope.launch{ ... }的协程,我想不会阻塞main UI吧?

新增内容:

致 Tenfour04:谢谢!

我显示函数soundDb()soundDb()是挂起函数,但可能需要运行长时间!您认为该应用程序是否会导致“应用程序无响应”错误?

代码A

class HandleMeter: ViewModel() {
    var myInfo = mutableStateOf("Hello")
    var a = 1

    fun calCurrentAsyn() {
        viewModelScope.launch {
            var b = 0.0
            for (i in 1..5) {
                b = b + soundDb()
                delay(100)
            }
            b = b / 5.0
            myInfo.value = b.toString() + " OK Asyn  " + a++.toString()
        }
    }

}

suspend fun soundDb(): Double {
    var k=0.0
    for (i in 1..500000000){
        k=k+i
    }
    return k
}

第二个添加的内容:

致Tenfour04:非常感谢!

Code B和Code C哪个更好,或者它们是一样的?

代码B

class HandleMeter: ViewModel() {
    var myInfo = mutableStateOf("Hello")
    var a = 1

    fun calCurrentAsyn() {
        viewModelScope.launch (Dispatchers.Default){
            var b = 0.0
            for (i in 1..5) {
                b = b + soundDb()
                delay(100)
            }
            b = b / 5.0
            myInfo.value = b.toString() + " OK Asyn  " + a++.toString()
        }
    }

}

suspend fun soundDb(): Double {
    var k=0.0
    for (i in 1..500000000){
        k=k+i
    }
    return k
}

代码C

class HandleMeter: ViewModel() {
    var myInfo = mutableStateOf("Hello")
    var a = 1

    fun calCurrentAsyn() {
        viewModelScope.launch {
            var b = 0.0
            for (i in 1..5) {
                b = b + soundDb()
                delay(100)
            }
            b = b / 5.0
            myInfo.value = b.toString() + " OK Asyn  " + a++.toString()
        }
    }

}

suspend fun soundDb(): Double = withContext(Dispatchers.Default) {
    var k=0.0
    for (i in 1..500000000){
        k=k+i
    }
    return@withContext k  //I fixed it
}

viewModelScope 启动的协程默认在主调度器上运行。只要您不在其中调用任何阻塞函数,它就不会阻塞主线程。如果需要调用阻塞函数,可以将其包裹在withContext中,并指定一个非Dispatchers.Main.

的Dispatcher

我在您的代码中看到的唯一可疑之处是 soundDb()。如果那是一个阻塞函数,那么这个协程阻塞主线程。

函数可以属于三个类别之一。

  • 常规non-blocking
  • 常规屏蔽
  • 暂停(non-blocking)

可以创建一个阻塞的挂起函数,但按照惯例这是被禁止的,因为它会造成混淆并且违背了拥有挂起函数的目的。如果编译器能够检测到挂起函数中的阻塞函数调用,它会发出警告。并非所有阻塞函数都可以被编译器检测到。

编辑

增加 5 亿次不会花费足够的时间来导致 ANR 错误,但它可能会导致 UI 出现明显的卡顿。当你做类似 long-running 计算的事情时,它可以被认为是阻塞的,所以这将是一个无效的挂起函数。您可以通过将阻塞工作包装在 withContext 中来修复它,以便它使用可以处理阻塞工作的 Dispatcher。现在挂起函数是 non-blocking(按照惯例应该始终是你编写挂起函数的方式)所以从任何协程调用都是安全的,不管协程当前使用的是什么调度程序。

suspend fun soundDb(): Double = withContext(Dispatchers.Default) {
    var k=0.0
    for (i in 1..500000000){
        k=k+i
    }
    k
}