【Android Audio Focus 音频焦点】

本文介绍了Android中的AudioFocus机制,如何处理不同类型的焦点事件(如GAIN、LOSS、TRANSIENT),以及如何在代码中实现音频焦点的请求、放弃和相应的播放控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

介绍

Android 中的音频焦点(Audio Focus)是一种机制,用于管理应用程序之间的音频资源竞争。当多个应用程序同时请求使用音频设备时,通过音频焦点机制可以确保最终用户的体验不受影响。

两个或两个以上的 Android 应用可同时向同一输出流播放音频。系统会将所有音频流混合在一起。虽然这是一项出色的技术,但却会给用户带来很大的困扰。为了避免所有音乐应用同时播放,Android 引入了“音频焦点”的概念。 一次只能有一个应用获得音频焦点。

当您的应用需要输出音频时,它需要请求获得音频焦点,获得焦点后,就可以播放声音了。不过,在您获得音频焦点后,您可能无法将其一直持有到播放完成。其他应用可以请求焦点,从而占有您持有的音频焦点。如果发生这种情况,您的应用应暂停播放或降低音量,以便于用户听到新的音频源。

音频焦点采用合作模式。我们建议应用遵守音频焦点准则,但系统不会强制执行这些准则。如果应用想要在失去音频焦点后继续大声播放,系统无法阻止它。这是一种不好的体验,用户很可能会卸载具有这种不良行为的应用。


焦点事件

  • AUDIOFOCUS_GAIN
    说明: 应用获得了音频焦点,可以继续播放音频。
    处理方式: 在这个状态下,应用可以正常播放音频。在失去焦点后再次获取焦点时,通常需要在这个状态下恢复播放。

  • AUDIOFOCUS_LOSS
    说明: 【永久性失去焦点】应用失去了音频焦点,需要停止音频播放。
    处理方式: 当收到 AUDIOFOCUS_LOSS 时,应用应该停止播放音频,并释放相关资源。这种情况通常是由于其他应用请求了长时间的音频焦点,例如开始播放长时间的音频文件。不要立即尝试重新获取焦点。

  • AUDIOFOCUS_LOSS_TRANSIENT
    说明: 【暂时性失去焦点】应用短暂失去了音频焦点,但可以在稍后重新获取焦点。
    处理方式: 当收到 AUDIOFOCUS_LOSS_TRANSIENT 时,应用应该暂停音频播放。这种情况通常是由于短暂的通知声、铃声等情况导致的。应用通常会在稍后重新获取焦点并恢复播放。

  • AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
    说明: 【暂时性失去焦点】应用短暂失去了音频焦点,但可以降低音量继续播放。稍后可以重新获取焦点。
    处理方式: 当收到 AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK 时,应用可以选择降低音量而不是完全停止音频播放。这种情况通常用于在接收短暂通知时,应用可以继续以较低的音量播放。

暂时性失去焦点:

在暂时性失去音频焦点时,您应该继续监控音频焦点的变化,并准备好在重新获得焦点后恢复正常播放。当抢占焦点的应用放弃焦点时,您会收到一个回调 (AUDIOFOCUS_GAIN)。此时,您可以在此回调中,将音量恢复到正常水平或重新开始播放。

永久性失去焦点:

如果是永久性失去音频焦点 (AUDIOFOCUS_LOSS),则其他应用会播放音频。您的应用应立即暂停播放,因为它不会收到 AUDIOFOCUS_GAIN 回调。要想重新开始播放,用户必须执行明确的操作,例如在通知或应用界面中按播放传输控件。


代码示例

Android 8.0 及更高版本中的音频焦点:

从 Android 8.0(API 级别 26)开始,当您调用 requestAudioFocus() 时,必须提供 AudioFocusRequest 参数。要释放音频焦点,请调用 abandonAudioFocusRequest() 方法,该方法也接受 AudioFocusRequest 作为参数。在请求和放弃焦点时,应使用相同的 AudioFocusRequest 实例。

要创建 AudioFocusRequest,请使用 AudioFocusRequest.Builder。由于焦点请求始终必须指定请求的类型,因此此类型会包含在构建器的构造函数中。使用构建器的方法来设置请求的其他字段。

    audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
    focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN).run {
        setAudioAttributes(AudioAttributes.Builder().run {
            setUsage(AudioAttributes.USAGE_MEDIA)//or AudioAttributes.USAGE_GAME
            setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
            build()
        })
        setAcceptsDelayedFocusGain(true)
        setOnAudioFocusChangeListener(afChangeListener, handler)
        build()
    }

    val focusLock = Any()

    var playbackDelayed = false
    var playbackNowAuthorized = false

    // ...
    val res = audioManager.requestAudioFocus(focusRequest)
    synchronized(focusLock) {
        playbackNowAuthorized = when (res) {
            AudioManager.AUDIOFOCUS_REQUEST_FAILED -> false
            AudioManager.AUDIOFOCUS_REQUEST_GRANTED -> {
                playbackNow()
                true
            }
            AudioManager.AUDIOFOCUS_REQUEST_DELAYED -> {
                playbackDelayed = true
                false
            }
            else -> false
        }
    }

	val afChangeListener = AudioManager.OnAudioFocusChangeListener { focusChange ->
        when (focusChange) {
            AudioManager.AUDIOFOCUS_LOSS -> {
                // Permanent loss of audio focus. Pause playback immediately
                synchronized(focusLock) {
                    resumeOnFocusGain = false
                    playbackDelayed = false
                }
                pausePlayback()
            }
            AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
                // Pause playback
                synchronized(focusLock) {
                    resumeOnFocusGain = true
                    playbackDelayed = false
                }
                pausePlayback()
            }
            AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
                // ... pausing or Lower the volume depends on your app
                
            }
            AudioManager.AUDIOFOCUS_GAIN -> {
                // Your app has been granted audio focus again
                // Raise volume to normal, restart playback if necessary
                if (playbackDelayed || resumeOnFocusGain) {
                    synchronized(focusLock) {
                        playbackDelayed = false
                        resumeOnFocusGain = false
                    }
                    playbackNow()
                }
            }
        }
    }

官方文档链接管理音频焦点

### Android 音频焦点请求实现 在 Android 中,音频焦点管理用于协调多个应用之间的音频播放行为。为了确保良好的用户体验,在播放媒体时需遵循音频焦点的相关规则。 #### 使用 `AudioFocusRequest` 请求音频焦点Android 8.0 (API Level 26) 开始,必须通过创建 `AudioFocusRequest.Builder` 来构建并传递一个 `AudioFocusRequest` 对象给 `requestAudioFocus()` 方法[^1]。以下是具体实现: ```java import android.media.AudioAttributes; import android.media.AudioFocusRequest; import android.media.AudioManager; public class AudioFocusExample { private AudioManager audioManager; private AudioFocusRequest audioFocusRequest; public void initAudioFocus(Context context) { audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); // 创建 AudioAttributes AudioAttributes attributes = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .build(); // 构建 AudioFocusRequest audioFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN) .setOnAudioFocusChangeListener(focusChange -> handleAudioFocusChange(focusChange)) .setAudioAttributes(attributes) .build(); } public int requestAudioFocus() { return audioManager.requestAudioFocus(audioFocusRequest); } public void abandonAudioFocus() { audioManager.abandonAudioFocusRequest(audioFocusRequest); } private void handleAudioFocusChange(int focusChange) { switch (focusChange) { case AudioManager.AUDIOFOCUS_LOSS: // 失去焦点,停止播放 break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: // 短暂失去焦点,暂停播放 break; case AudioManager.AUDIOFOCUS_GAIN: // 获取到焦点,恢复播放 break; default: break; } } } ``` 上述代码展示了如何初始化 `AudioFocusRequest` 并请求/释放音频焦点。需要注意的是,`requestAudioFocus()` 和 `abandonAudioFocusRequest()` 的调用均依赖于同一个 `AudioFocusRequest` 实例。 #### 底层原理分析 当应用程序请求音频焦点时,实际会触发底层服务的调用链路。最终,这一过程由 `IAudioService` 接口完成,并通过 `AudioService` 类中的 `requestAudioFocus()` 方法来执行具体的逻辑[^2]。 对于更复杂的场景(如 Android 9 及以上版本),可以通过扩展 API 提供额外的功能支持。例如,通知外部策略变化的方法 `notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo())` 是内部实现的一部分[^3]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ta叫我小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值