简体   繁体   English

如何从后台线程在 UI 线程上运行并等待结果?

[英]How to run on UI thread from a background thread and wait for result?

I have a third party library that periodically queries my video player (ExoPlayer) for information such as current position in the video.我有一个第三方库,它会定期查询我的视频播放器 (ExoPlayer) 以获取诸如视频中当前位置之类的信息。 This third party library runs on a background thread.这个第三方库在后台线程上运行。 Problem is, the ExoPlayer instance is not allowed to be accessed by a background thread.问题是,后台线程不允许访问 ExoPlayer 实例。

One of my ideas was to use coroutines to force a switch to the main thread before accessing the ExoPlayer instance.我的一个想法是使用协程在访问 ExoPlayer 实例之前强制切换到主线程。 Something like this (note that this is called from multiple places, both on main and background threads):像这样的事情(请注意,这是从主线程和后台线程上的多个地方调用的):

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
    }
}

Then I would call this with runBlocking like so:然后我会像这样用 runBlocking 调用它:

runBlocking { videoPlayerWrapper.getCurrentPosition() }

runBlocking is used because the third party library expects an instant result.使用 runBlocking 是因为第三方库需要即时结果。

This works sometimes but other times my app is completely locking up.这有时有效,但有时我的应用程序完全锁定。 Any idea what might be wrong?知道可能有什么问题吗? Any alternative solutions?任何替代解决方案?

runBlocking is a suspend function that blocks current thread until coroutine launched is completed. runBlocking是一个挂起函数,它会阻塞当前线程,直到协程启动完成。 I guess that you are blocking main thread with this call.我猜你用这个调用阻塞了主线程。 I suggest you to use a function like this.我建议你使用这样的函数。

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

The Dispatchers.Main.immediate switches context only if current thread is not the main thread. Dispatchers.Main.immediate仅在当前线程不是主线程时才切换上下文。

Then I would call this with runBlocking like so:然后我会像这样用 runBlocking 调用它:

runBlocking { videoPlayerWrapper.getCurrentPosition() } runBlocking { videoPlayerWrapper.getCurrentPosition() }

By definition runBlocking will block the current Thread until the coroutine completes.根据定义runBlocking将阻塞当前Thread直到协程完成。 From the runBlocking docs :runBlocking 文档

Runs a new coroutine and blocks the current thread interruptibly until its completion.运行一个新的协程并中断地阻塞当前线程直到它完成。 This function should not be used from a coroutine.不应在协程中使用此函数。 It is designed to bridge regular blocking code to libraries that are written in suspending style, to be used in main functions and in tests.它旨在将常规阻塞代码桥接到以挂起风格编写的库,以用于主要功能和测试。

This effectively means that your UI will freeze if you are calling runBlocking from the main Thread .这实际上意味着如果您从主Thread调用runBlocking ,您的 UI 将冻结。 My guess is that sometimes videoPlayerWrapper.getCurrentPosition() returns fast enough for you not to notice that is actually blocking.我的猜测是有时videoPlayerWrapper.getCurrentPosition()返回的速度足够快,以至于您不会注意到实际上是在阻塞。

This third party library runs on a background thread.这个第三方库在后台线程上运行。 Problem is, the ExoPlayer instance is not allowed to be accessed by a background thread.问题是,后台线程不允许访问 ExoPlayer 实例。

It seems to me that there is a design flaw here.在我看来,这里有一个设计缺陷。 Rather than the library trying to get a hold of the Exoplayer instance, the library should establish a contract for the consumer to provide the position.图书馆不应试图获取Exoplayer实例,而应为消费者建立合同以提供位置。 If using coroutines, the library could require a Flow<Int> that emits the current position as the position changes.如果使用协程,库可能需要一个Flow<Int>在位置变化时发出当前位置。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM