CoroutineScope(job+Dispatchers.Main) 运行 如何在 main/UI 线程上?

How can CoroutineScope(job+Dispatchers.Main) run on the main/UI thread?

如果 CoroutineScope(job+Dispatchers.Main){...} 运行 内的操作在主线程上那么它怎么不违反 Android 的慢速(阻塞)操作(网络等)的要求不允许在 main/UI 线程上 运行?我可以 运行 使用此范围阻止操作,并且 UI 根本不会冻结。

如果有人能解释幕后发生的事情,我将不胜感激。我的猜测是它类似于 JavaScript 如何使用事件循环管理阻塞操作,但我很难找到任何相关资料。

运行 阻塞操作和运行暂停操作CoroutineScope(Dispatchers.Main)是两个不同的东西.

delay()是一个挂起函数,它是非阻塞的

CoroutineScope(Dispatchers.Main){
    delay(6000)
}

虽然Thread.sleep()正在阻塞并且调用下面的代码会导致ANR

CoroutineScope(Dispatchers.Main){
    Thread.sleep(6000)
}

我建议您查看 Roman Elizarov 在 Kotlinconf 2017 上关于 Kotlin 协程的演讲,尤其是他运行 100,000 的部分 delay()

My guess is that it is similar to how JavaScript manages blocking operations with the event loop

是的,这是正确的,事件循环对于协同程序的工作至关重要。基本上,当你这样写的时候:

uiScope.launch {
    delay(1000)
    println("A second has passed")
}

编译成和下面效果一样的代码:

Handler(Looper.mainLooper()).postDelayed(1000) { println("A second has passed") }

主要概念是 continuation,一个实现状态机的对象,该状态机对应于您在可挂起函数中编写的顺序代码。当您调用 delay 或任何其他可暂停函数时,延续的入口点方法 return 是一个特殊的 COROUTINE_SUSPENDED 值。稍后,当某些外部代码得出可挂起函数的 return 值时,它必须调用 continuation.resume(result)。此调用将被负责的调度程序拦截,它将 post 此调用作为 GUI 事件循环中的一个事件。当事件处理程序出队并执行时,您将回到状态机内部,该状态机确定从哪里恢复执行。

您可以查看 this answer 以获得更充实的使用 Continuation API 的示例。