You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ExoPlayer2使用playWhenReady启停音频时currentPosition延迟问题求助

解决ExoPlayer暂停重启后currentPosition跳变的问题

从你的描述和日志来看,这个问题的核心是暂停播放时没有捕获到真正的最终暂停位置,再加上重启时播放器内部的同步延迟,导致获取到的currentPosition出现跳变,进而让进度条无法平滑更新。

问题原因拆解

  • 当你设置player!!.playWhenReady = false时,ExoPlayer不会立刻终止播放流程,可能还会播放缓冲内的几帧内容,这时候立刻读取的currentPosition并不是最终的暂停位置。
  • 重启播放时,直接读取player!!.currentPosition可能拿到的是播放器内部的缓存值,还没和实际播放进度同步,导致数值跳变。

解决方案

我们需要两个关键操作来解决这个问题:

  1. 暂停时,等待播放器真正进入暂停状态后,再记录准确的位置
  2. 重启播放前,手动将播放器进度跳转到暂停时记录的准确位置,再启动播放

修改后的代码示例

首先,添加一个变量来存储暂停时的准确位置:

private var pausedPosition: Long = 0L

然后修改暂停/播放的核心逻辑:

if (activeHolder!! == holder) {
    when (player!!.playWhenReady) {
        true -> {
            // 先临时记录当前位置,再监听播放器状态确认真正暂停
            pausedPosition = player!!.currentPosition
            player!!.playWhenReady = false
            
            // 添加状态监听,确保播放器完全暂停后再更新UI和记录最终位置
            player!!.addListener(object : Player.Listener {
                override fun onPlaybackStateChanged(state: Int) {
                    super.onPlaybackStateChanged(state)
                    if (state == Player.STATE_READY && !player!!.playWhenReady) {
                        // 此时播放器已完全暂停,更新准确的暂停位置
                        pausedPosition = player!!.currentPosition
                        showLog("实际暂停位置: $pausedPosition")
                        handlerChangeProgress.removeCallbacks(updateChangeProgressTask)
                        showAudioStartButton(activeHolder!!)
                        // 移除监听避免内存泄漏
                        player!!.removeListener(this)
                    }
                }
            })
            return
        }
        false -> {
            // 重启前先跳转到记录的暂停位置,再启动播放
            player!!.seekTo(pausedPosition)
            player!!.playWhenReady = true
            showLog("重启播放位置: $pausedPosition")
            handlerChangeProgress.postDelayed(updateChangeProgressTask, delayChangeProgress)
            showAudioStopButton(activeHolder!!)
            return
        }
    }
}

同时优化进度条更新逻辑,暂停状态下直接用我们记录的位置更新:

override fun onChangeProgressBar(progress: Float, currentPosition: Int, totalDuration: Int) {
    if(player!!.playWhenReady) {
        if (activeHolder?.adapterPosition == audioPosition) {
            activeHolder?.let {
                it.binding.progressBar.max = totalDuration
                it.binding.progressBar.progress = currentPosition
                showAudioStopButton(it)
            }
        }
    } else {
        // 暂停状态时,用记录的准确位置更新进度条
        activeHolder?.let {
            it.binding.progressBar.max = player!!.duration.toInt()
            it.binding.progressBar.progress = pausedPosition.toInt()
            showAudioStartButton(it)
        }
    }
}

额外优化建议

  • 可以提前定义一个全局的Player.Listener实例,避免每次暂停都创建新对象,减少内存开销
  • 在页面销毁时,记得移除所有播放器监听、清空handler的回调,避免内存泄漏

修改后,你的日志应该会显示暂停和重启的位置完全一致,进度条也能实现平滑的暂停-重启过渡。

内容的提问来源于stack exchange,提问作者Charles

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 20:52:47