【问题标题】:How can CoroutineScope(job+Dispatchers.Main) run on the main/UI thread?CoroutineScope(job+Dispatchers.Main) 如何在主/UI 线程上运行?
【发布时间】:2020-06-02 00:55:55
【问题描述】:

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

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

【问题讨论】:

  • I can run blocking operations with this scope and the UI does not freeze at all. 是什么意思?请举个例子?
  • @DmitriiLeonov,例如,我可以通过调用delay(1000) 来暂停线程。如果我在 UI 线程上执行此操作,这不会导致出现 ANR 屏幕吗?

标签: android kotlin-coroutines android-thread coroutinescope


【解决方案1】:

我的猜测是它类似于 JavaScript 使用事件循环管理阻塞操作的方式

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

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

它编译成与此具有相同效果的代码:

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

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

您可以查看this answer,了解使用Continuation API 的更详细示例。

【讨论】:

  • 感谢您的解释。我还没有看到任何人提供基于 Handler 的等效示例。
  • delay 函数是非阻塞的。 OP正在询问阻塞操作,例如一些图像处理逻辑。
  • @alekop OP:“我可以在这个范围内运行阻塞操作,并且 UI 根本不会冻结。” “例如,我可以通过调用 delay(1000) 来暂停线程” 这意味着他们在比通常的行话更普遍的意义上使用“阻塞”这个词。从这个意义上说,它也包括暂停行为。
【解决方案2】:

CoroutineScope(Dispatchers.Main)上运行阻塞操作和运行挂起操作是两个不同的东西。

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

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

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

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

我建议你查看 Roman Elizarov 在 Kotlinconf 2017 上的 Kotlin coroutines talk,尤其是他运行 100,000 的部分delay()

【讨论】:

  • 感谢您的见解!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-10-02
  • 1970-01-01
  • 2013-09-02
  • 2020-02-19
  • 2016-12-19
  • 2019-03-09
  • 1970-01-01
相关资源
最近更新 更多