如何在Getter中主线程调用ExoPlayer方法并正确获取返回值?
解决方案
前提判断
首先先确认你调用该Getter的线程:如果本身就是在主线程调用,不需要任何额外封装,直接返回属性即可:
val currentTrackIndex: Int get() = exoPlayer.currentIndex
如果是在子线程调用该Getter,需要同步获取结果,可以选择以下任意方案:
方案1:CountDownLatch实现(无额外依赖,兼容所有API版本)
利用CountDownLatch阻塞子线程,直到主线程执行完属性读取逻辑再返回结果,注意必须先做主线程判断避免死锁:
val currentTrackIndex: Int get() { // 主线程直接返回,避免死锁 if (Looper.myLooper() == Looper.getMainLooper()) { return exoPlayer.currentIndex } var result = -1 val latch = CountDownLatch(1) handler.post { result = exoPlayer.currentIndex latch.countDown() } latch.await() return result }
方案2:协程runBlocking实现(项目引入协程时优先选择)
你之前的runBlocking写法没有指定主线程调度器,所以不符合ExoPlayer的调用要求,修正后完全可以在生产环境使用:
val currentTrackIndex: Int get() { if (Looper.myLooper() == Looper.getMainLooper()) { return exoPlayer.currentIndex } return runBlocking(Dispatchers.Main.immediate) { exoPlayer.currentIndex } }
注:runBlocking仅会阻塞当前调用线程,只要读取属性的耗时极短,不会带来性能问题,也符合生产环境使用规范
方案3:系统API实现(minSdk ≥ 23)
API 23及以上系统自带了postAndAwait方法,不需要自己实现阻塞逻辑:
val currentTrackIndex: Int get() { if (Looper.myLooper() == Looper.getMainLooper()) { return exoPlayer.currentIndex } return handler.postAndAwait { exoPlayer.currentIndex } }
注意事项
- 所有方案都必须加主线程判断,否则在主线程调用Getter时会触发死锁:主线程被阻塞,post到主线程的任务永远无法执行。
- 不要在post到主线程的代码块中加入耗时操作,即使是在Service中,主线程阻塞超过5秒也会触发ANR,仅用来读取属性的场景完全符合要求。
- 非必要场景下更推荐使用异步逻辑:直接把需要用到该属性的逻辑post到主线程执行,不需要阻塞线程,性能更优。
内容的提问来源于stack exchange,提问作者Milen Pivchev
相关产品推荐
相关产品推荐

