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

AudioManager.OnAudioFocusChangeListener在AUDIOFOCUS_LOSS后失效的解决方法

音频焦点失效问题解决方案

核心问题分析

  1. 音频属性配置错误:多次调用setUsage会覆盖之前的设置,最终生效的是最后一个USAGE_ASSISTANCE_SONIFICATION,这会导致系统对音频焦点的处理逻辑不符合音乐播放器的预期。
  2. 焦点状态处理逻辑错误:收到AUDIOFOCUS_LOSS(永久失去焦点)时,代码反而执行播放操作,完全违背逻辑;且永久失焦后没有主动放弃焦点,也没有在后续需要时重新请求。
  3. 错误创建Activity实例:在监听器里直接new MusicActivity()是无效的,Activity不能通过这种方式实例化来控制播放器,会导致操作无法作用到实际运行的播放器上。
  4. 焦点监听器生命周期问题:audioFocusChangeListener是局部变量,可能被GC回收,需要改为Service的成员变量。

修复步骤及代码修改

1. 修正音频属性配置

音乐播放器只需设置USAGE_MEDIA,删除其他重复的setUsage调用:

playbackAttributes = AudioAttributes.Builder()
    .setUsage(AudioAttributes.USAGE_MEDIA)
    .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
    .build()

2. 修正焦点状态处理逻辑

重新梳理各焦点状态的正确行为,同时将监听器改为Service成员变量避免被回收:

// 声明为Service成员变量
private lateinit var audioFocusChangeListener: AudioManager.OnAudioFocusChangeListener

override fun onCreate() {
    super.onCreate()
    val idNotification = (System.currentTimeMillis() % 10000).toInt()
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        startForeground(idNotification, Notification.Builder(this, NotificationService.channelId).build())

        audioManager = getSystemService(AUDIO_SERVICE) as AudioManager
        playbackAttributes = AudioAttributes.Builder()
            .setUsage(AudioAttributes.USAGE_MEDIA)
            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
            .build()

        audioFocusChangeListener = AudioManager.OnAudioFocusChangeListener { focusChange ->
            Log.d("AudioListener", focusChange.toString())

            when (focusChange) {
                AudioManager.AUDIOFOCUS_GAIN -> {
                    // 获得焦点,恢复播放
                    if (!resting && player != null && !player!!.isPlaying) {
                        player!!.start()
                    }
                }
                AudioManager.AUDIOFOCUS_LOSS_TRANSIENT, AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
                    // 临时失焦,暂停播放
                    if (player != null && player!!.isPlaying) {
                        player!!.pause()
                    }
                }
                AudioManager.AUDIOFOCUS_LOSS -> {
                    // 永久失焦,暂停并放弃焦点
                    if (player != null && player!!.isPlaying) {
                        player!!.pause()
                    }
                    audioManager.abandonAudioFocusRequest(audioFocusRequest)
                    resting = true
                }
            }
        }

        val focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
            .setAudioAttributes(playbackAttributes)
            .setAcceptsDelayedFocusGain(true)
            .setOnAudioFocusChangeListener(audioFocusChangeListener)
            .build()

        audioFocusRequest = audioManager.requestAudioFocus(focusRequest)
    }
}

3. 移除错误的Activity实例化

不要在Service里直接创建Activity对象,播放器的控制逻辑直接放在Service内部维护,通过Service自身的player实例调用start()/pause()方法。

4. 添加重新请求焦点的逻辑

当用户在永久失焦后再次触发播放(比如通知栏按钮点击),需要重新请求音频焦点:

// 在Service中添加播放恢复方法
fun resumePlayback() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
            .setAudioAttributes(playbackAttributes)
            .setAcceptsDelayedFocusGain(true)
            .setOnAudioFocusChangeListener(audioFocusChangeListener)
            .build()
        val result = audioManager.requestAudioFocus(focusRequest)
        if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
            player?.start()
            resting = false
        }
    }
}

5. 完善Service生命周期管理

在onDestroy中释放音频焦点和播放器资源:

override fun onDestroy() {
    super.onDestroy()
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        audioManager.abandonAudioFocusRequest(audioFocusRequest)
    }
    player?.release()
    player = null
}

关键注意事项

  • 音频焦点的请求和放弃必须成对出现,永久失焦后一定要调用abandonAudioFocusRequest,否则后续无法正常重新请求。
  • Service内的播放器状态要统一维护,避免和Activity状态不一致。
  • 针对Android O及以上版本,必须使用AudioFocusRequest而非旧的requestAudioFocus方法。

内容的提问来源于stack exchange,提问作者Pup In The Tree

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 17:12:42