Почему сопрограммы kotlin могут управлять элементами пользовательского интерфейса в другом потоке - PullRequest
2 голосов
/ 25 апреля 2019

Я пытаюсь 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, то есть в потоке пользовательского интерфейса.Однако приведенный выше код не будет генерировать исключение после того, как мы установим для этого рабочего потока текст text_view, а для параметра "test" будет правильно установлено значение text_view.В чем причина?

Обновление 1:

Добавление delay(10000L) перед setText() вызовет исключение при более коротком времени (например, 1000L в моем тесте для отладочного прогона)с холодным запуском) не будет.Так что похоже на проблему с Android.Но все же этот вопрос, в чем причина?

Обновление 2:

Теперь я понял, что это поведение связано с Android, а не с kotlinx.coroutines.Код выше выполняется в onCreate(), когда ViewRootImpl может не вызвать performTraversals() или инициализировать все View s.В этой ситуации, checkThread() до операции пользовательского интерфейса также не вызывается.

Ответы [ 2 ]

0 голосов
/ 25 апреля 2019

Ничего общего с сопрограммами Kotlin на самом деле.

Даже если вам не следует вызывать функции пользовательского интерфейса из потоков, не относящихся к пользовательскому интерфейсу, не каждая функция пользовательского интерфейса Android фактически проверяет, что вы находитесь в потоке пользовательского интерфейса.TextView#setText() является одним из них, и вы можете избежать вызова из фонового потока без исключения.

0 голосов
/ 25 апреля 2019

Диспетчер по умолчанию, который используется, когда сопрограммы запускаются в GlobalScope, представлен как Dispatchers.Default и использует общий фоновый пул потоков, поэтому launch(Dispatchers.Default) { ... } использует тот же диспетчер, что и GlobalScope.launch { ... }.

Следовательно, когда launch { ... } используется без параметров, он наследует контекст (и, следовательно, диспетчер) от CoroutineScope, из которого он запускается.

В этом случае он наследует контекст основного потока.


Таким образом, если мы не определим context & dispatcher , Coroutine будет работать с основным потоком, создавая новый рабочий поток из DefaultDispatcher (в нашем случае снова main) .

...