Android应用恢复视频时ExoPlayer音频焦点丢失的处理方案
ExoPlayer音频焦点恢复异常的解决方案
针对你遇到的恢复播放后短暂停止、提示PLAY_WHEN_READY_CHANGE_REASON_AUDIO_FOCUS_LOSS的问题,以下是具体的修复建议:
1. 优化音频焦点请求逻辑
不要在恢复播放时直接调用player.play(),而是先主动请求音频焦点,且使用永久获取的类型(AUDIOFOCUS_GAIN),确保系统明确分配焦点到你的应用:
private val audioManager by lazy { getSystemService(Context.AUDIO_SERVICE) as AudioManager } private val audioFocusRequest by lazy { AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN) .setOnAudioFocusChangeListener { focusChange -> handleAudioFocusChange(focusChange) } .setAudioAttributes( AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .setContentType(AudioAttributes.CONTENT_TYPE_MOVIE) .build() ) .build() } // 恢复播放前先请求焦点 fun resumePlayer() { val focusResult = audioManager.requestAudioFocus(audioFocusRequest) if (focusResult == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { player.play() } }
2. 精准处理音频焦点状态变化
在焦点回调中,区分临时丢失和永久丢失,避免误暂停;同时在重新获得焦点时,确保只有当播放器是因焦点丢失而暂停的情况下才恢复:
private var wasPausedByAudioFocusLoss = false private fun handleAudioFocusChange(focusChange: Int) { when (focusChange) { AudioManager.AUDIOFOCUS_LOSS -> { // 永久丢失焦点,彻底暂停并标记状态 wasPausedByAudioFocusLoss = true player.pause() } AudioManager.AUDIOFOCUS_LOSS_TRANSIENT, AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> { // 临时丢失焦点,暂停但保留播放状态 wasPausedByAudioFocusLoss = true player.pause() } AudioManager.AUDIOFOCUS_GAIN -> { // 重新获得焦点,且之前是因焦点丢失暂停的,才恢复播放 if (wasPausedByAudioFocusLoss) { player.play() wasPausedByAudioFocusLoss = false } } } }
3. 结合应用生命周期调整焦点管理
在应用从后台返回前台(onResume)时,不要直接恢复播放,而是先检查当前音频焦点状态,必要时重新请求:
override fun onResume() { super.onResume() // 检查当前是否持有音频焦点 val currentFocusStatus = audioManager.getAudioFocusRequest(audioFocusRequest) if (currentFocusStatus != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { // 重新请求焦点后再恢复 resumePlayer() } else { // 已持有焦点,直接恢复播放 player.play() } } override fun onPause() { super.onPause() // 后台时放弃焦点,避免干扰其他应用 audioManager.abandonAudioFocusRequest(audioFocusRequest) }
4. 处理第三方应用焦点残留问题
部分浏览器或视频应用暂停后可能未正确释放音频焦点,导致系统焦点状态延迟更新。可以在恢复播放前添加短暂延迟,或重复请求焦点:
fun resumePlayerWithRetry() { Handler(Looper.getMainLooper()).postDelayed({ val focusResult = audioManager.requestAudioFocus(audioFocusRequest) if (focusResult == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { player.play() } else { // 重试一次,适配焦点状态延迟更新的情况 Handler(Looper.getMainLooper()).postDelayed({ if (audioManager.requestAudioFocus(audioFocusRequest) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { player.play() } }, 500) } }, 300) }
5. 统一ExoPlayer音频属性配置
确保ExoPlayer使用的音频属性与焦点请求一致,避免系统因属性不匹配收回焦点:
val audioAttributes = AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .setContentType(AudioAttributes.CONTENT_TYPE_MOVIE) .build() player.setAudioAttributes(audioAttributes, true)
内容的提问来源于stack exchange,提问作者Giru Bhai
相关产品推荐
相关产品推荐

