Как запустить поток пользовательского интерфейса из фонового потока и дождаться результата? - PullRequest
0 голосов
/ 09 января 2020

У меня есть сторонняя библиотека, которая периодически запрашивает у моего видеопроигрывателя (ExoPlayer) такую ​​информацию, как текущая позиция в видео. Эта сторонняя библиотека работает в фоновом потоке. Проблема в том, что фоновый поток не может получить доступ к экземпляру ExoPlayer.

Одна из моих идей заключалась в том, чтобы использовать сопрограммы для принудительного переключения на основной поток перед доступом к экземпляру ExoPlayer. Примерно так (обратите внимание, что это вызывается из нескольких мест, как в основном, так и в фоновом потоках):

suspend fun getCurrentPosition(): Long {
    if (Looper.myLooper() != Looper.getMainLooper()) {
        // On a background thread, switch to main thread and return current position
        return withContext(Dispatchers.Main) {
            exoPlayerInstance.currentPosition
        }
    } else {
        // Already on main thread, no need to switch threads
        return exoPlayerInstance.currentPosition
    }
}

Тогда я бы назвал это с помощью runBlocking следующим образом:

runBlocking { videoPlayerWrapper.getCurrentPosition() }

runBlocking используется, потому что сторонняя библиотека ожидает мгновенный результат.

Иногда это работает, но в других случаях мое приложение полностью блокируется. Есть идеи, что может быть не так? Есть альтернативные решения?

Ответы [ 2 ]

0 голосов
/ 10 января 2020

runBlocking - это функция приостановки, которая блокирует текущий поток до завершения запуска сопрограммы. Я предполагаю, что вы блокируете главный поток этим вызовом. Я предлагаю вам использовать такую ​​функцию.

  fun getCurrentPosition() = runBlocking(Dispatchers.Main.immediate) {
      exoPlayerInstance.currentPosition
  }

Dispatchers.Main.immediate переключает контекст, только если текущий поток не является основным потоком.

0 голосов
/ 09 января 2020

Тогда я бы назвал это с помощью runBlocking следующим образом:

runBlocking {videoPlayerWrapper.getCurrentPosition ()}

По определению runBlocking заблокирует текущий Thread пока сопрограмма не завершится. Из runBlocking документов :

Запускает новую сопрограмму и прерывает текущий поток до его завершения. Эта функция не должна использоваться из сопрограммы. Он предназначен для связывания обычного кода блокировки с библиотеками, написанными в стиле приостановки, для использования в основных функциях и в тестах.

Это фактически означает, что ваш пользовательский интерфейс будет зависать, если вы звоните runBlocking с основной Thread. Я предполагаю, что иногда videoPlayerWrapper.getCurrentPosition() возвращается достаточно быстро, чтобы вы не заметили, что это на самом деле блокировка.

Эта сторонняя библиотека работает в фоновом потоке. Проблема в том, что экземпляру ExoPlayer не разрешен доступ к фоновому потоку.

Мне кажется, здесь есть недостаток дизайна. Вместо того, чтобы библиотека пыталась завладеть экземпляром Exoplayer, библиотека должна заключить sh договор для потребителя на предоставление позиции. При использовании сопрограмм библиотеке может потребоваться Flow<Int>, который выдает текущую позицию при ее изменении.

...