为什么Kotlin协程可以在另一个线程上操作UI元素

时间:2019-04-25 12:41:03

标签: android multithreading kotlin coroutine kotlin-coroutines

我正在尝试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_view并且正确地将“ test”设置为text_view之后,上面的代码不会引发异常。是什么原因?

更新1:

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

更新2:

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

3 个答案:

答案 0 :(得分:0)

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

因此,当使用launch { ... }时不带参数时,它将从CoroutineScope继承了上下文 (并因此调度程序)从。

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


因此,除非我们定义 context&dispatcher ,否则协程将在主线程上工作,从DefaultDispatcher (在我们的情况下,再次是main)中创建新的工作线程

答案 1 :(得分:0)

与Kotlin协程无关。

即使您不应该从非UI线程调用UI函数,但并非每个Android UI函数实际上都会检查您是否在UI线程上。 TextView#setText()是其中之一,您可以从后台线程调用它而不会遇到异常。

答案 2 :(得分:0)

 GlobalScope.launch(Dispatchers.Main) {
            mTvText?.text = "text" // exemple: set text in main thread
            ... // write also your code here
        }