【问题标题】:Why can kotlin coroutines operate UI elements on another thread为什么kotlin协程可以在另一个线程上操作UI元素
【发布时间】:2019-09-14 21:37:42
【问题描述】:

我正在尝试 kotlinx.coroutines(版本:1.2.0)。下面是一个简单的测试代码块:

GlobalScope.launch {
  Logger.i("${Thread.currentThread()}, ${Looper.myLooper() == Looper.getMainLooper()}")
  text_view.text = "test"
}

打印出来的日志是:

Thread[DefaultDispatcher-worker-2,5,main], false

如日志所示,我们不在 Android 主线程,即 UI 线程。但是,当我们在这个工作线程上将 text 设置为 text_view 并且“test”正确设置为 text_view 后,上面的代码不会抛出异常。是什么原因?

更新 1:

setText() 之前添加delay(10000L) 会导致异常,而较短的时间(例如在我的冷启动调试运行测试中的1000L)不会。所以这似乎是一个Android问题。但是还是那个问题,是什么原因呢?

更新 2:

现在我意识到这种行为与 Android 而不是 kotlinx.coroutines 有关。当ViewRootImpl 可能没有调用performTraversals() 或初始化所有Views 时,上面的代码在onCreate() 中执行。在这种情况下,也不会调用 UI 操作之前的 checkThread()

【问题讨论】:

  • 您需要从Dispatcher 提供主线程,这将使其在主线程上运行。
  • 这不是问题,对吧?他在问为什么这种行为是可能的。
  • @ywwynm 你在用CorountineScope吗?
  • @BramSinke 不。我只使用上面显示的代码。
  • 那么,在 Update 2: 之后,您似乎找到了答案,对吧?

标签: android multithreading kotlin coroutine kotlin-coroutines


【解决方案1】:

GlobalScope中启动协程时使用的默认调度程序由Dispatchers.Default表示,并使用共享后台线程池,因此launch(Dispatchers.Default) { ... } 使用与GlobalScope.launch { ... } 相同的调度程序。

因此,当 launch { ... } 不带参数使用时,它会从启动它的 CoroutineScope 继承 context (以及调度程序)

在这种情况下,它继承了主线程的上下文。


所以,除非我们定义 context & dispatcher,否则 Coroutine 将在主线程上工作,从 DefaultDispatcher 创建新的工作线程 (在我们的例子中再次是 main)

【讨论】:

  • 对不起,我不认为你是对的。请参阅我的第一个问题更新。
  • 您可以参考这里吗? kotlinlang.org/docs/reference/coroutines/…,如果还有任何疑问,请告诉我。
  • 我不得不说我也尝试过GlobalScope.launch(Dispatchers.IO) 或其他调度程序或类似newSingleThreadContext 的东西。这是相同的结果。我只是简化了问题的代码。
  • 只是为了确保您的项目中有implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.0',这也让您可以访问Android Dispatchers.Main coroutine dispatcher
  • 在 GlobalScope 中启动协程时使用的默认调度程序由 Dispatchers.Default 表示,并使用共享后台线程池,因此 launch(Dispatchers.Default) { ... } 使用与 GlobalScope.launch { ... } 相同的调度程序。
【解决方案2】:

确实与 Kotlin 协程无关。

尽管您不应该从非 UI 线程调用 UI 函数,但并不是每个 Android UI 函数都会真正检查您是否在 UI 线程上。 TextView#setText() 就是其中之一,您可以从后台线程调用它而不会出现异常。

【讨论】:

【解决方案3】:
 GlobalScope.launch(Dispatchers.Main) {
            mTvText?.text = "text" // exemple: set text in main thread
            ... // write also your code here
        }

【讨论】:

  • 虽然此代码 sn-p 可能是解决方案,但包含解释确实有助于提高帖子的质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。
猜你喜欢
  • 2018-12-08
  • 1970-01-01
  • 1970-01-01
  • 2012-05-14
  • 1970-01-01
  • 1970-01-01
  • 2021-01-12
  • 2016-05-08
  • 2021-12-22
相关资源
最近更新 更多