Android P 音频焦点管理


开始之前先来一个 Google Developer 链接镇楼,本文主要基于Android P版本的音频焦点机制的梳理学习

什么是音频焦点管理

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

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

音频焦点采用合作模式。建议应用遵守音频焦点准则,但系统不会强制执行这些准则。如果应用想要在失去音频焦点后继续大声播放,系统无法阻止它。在这种情况下,就会给用户造成一种不好的体验。

音频焦点的申请

android 8.0前后有差异,我们主要看android 8.0及其以后的

//1、先获取一个AudioManager
audioManager = (AudioManager) Context.getSystemService(Context.AUDIO_SERVICE);


//2、创建AudioAttributes,AudioAttributes 描述了应用的用例。系统会在应用获得和失去音频焦点时查看这些属性。这些属性取代了音频流类型的概念。
mAudioAttributes = new AudioAttributes.Builder()
        .setUsage(AudioAttributes.USAGE_MEDIA)
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .build();

//3、创建AudioFocusRequest,其中有如下重要的字段
mFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
        .setFocusGain(AudioManager.AUDIOFOCUS_GAIN)
        .setAudioAttributes(mAudioAttributes)	
        .setWillPauseWhenDucked(true)
        .setAcceptsDelayedFocusGain(true)
        .setOnAudioFocusChangeListener(new AudioManager.OnAudioFocusChangeListener() {
            @Override
            public void onAudioFocusChange(int focusChange) {
                Log.d("kevin", " foucs change type = " + focusChange);
                switch (focusChange) {
                    case AudioManager.AUDIOFOCUS_LOSS:
                        Log.d("kevin", "AudioManager.AUDIOFOCUS_LOSS");
                        //失去焦点,暂停处理,暂停播放当前音乐
                        //你会长时间的失去焦点,所以不要指望在短时间内能获得。请结束自己的相关音频工作并做好收尾工作。比如另外一个音乐播放器开始播放音乐了,前提是这个另外的音乐播放器他也实现了音频焦点的控制,
                        break;
                    case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                        Log.d("kevin", "AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK");
                        //你的焦点会短暂失去,但是你可以与新的使用者共同使用音频焦点
                        break;
                    case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                        Log.d("kevin", "AudioManager.AUDIOFOCUS_LOSS_TRANSIENT");
                        //你会短暂的失去音频焦点,你可以暂停音乐,但不要释放资源,因为你一会就可以夺回焦点并继续使用
                        //如听音乐过程中,收到电话
                        break;
                    case AudioManager.AUDIOFOCUS_GAIN:
                        Log.d("kevin", "AudioManager.AUDIOFOCUS_GAIN");
                        //播放操作
                        //你已经完全获得了音频焦点
                        break;
                    default:
                        Log.d("kevin", "Unknown audio focus change code");
                }
            }
        })
        .build();

//4、请求获得音频焦点        
audioManager.requestAudioFocus(mFocusRequest);

看了上述代码,可能会有几个疑问:
1、音频流类型的概念,Android为不同的应用在不同的场合定义了不同的流类型
电话:STREAM_VOICE_CALL
系统:STREAM_SYSTEM
铃声:STREAM_RING
音乐:STREAM_MUSIC
闹钟:STREAM_ALARM
通知:STREAM_NOTIFICATION
蓝牙:STREAM_BLUETOOTH_SCO
其他国家的提示音:STREAM_SYSTEM_ENFORCED
双音多频:STREAM_DTMF
TTS: STREAM_TTS
这部分Stream Type可以自己扩展

2、setFocusGain() 每个请求中必要的字段,可以理解为告诉系统,该应用需要使用焦点多长时间
AUDIOFOCUS_GAIN 长时间持有音频焦点
AUDIOFOCUS_GAIN_TRANSIENT 只希望在短时间内播放音频,类似通知
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 只希望在短时间内播放音频,并允许前一个持有焦点的应用在降低其音频输出的情况下继续播放,此参数会触发其他监听器的AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE 用于表示对音频焦点的临时请求,类似录音等请求操作

3、setWillPauseWhenDucked() 当其他应用使用 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 请求焦点时,持有焦点的应用通常不会收到 onAudioFocusChange() 回调,因为系统可以自行降低音量。如果您需要暂停播放而不是降低音量,请调用 setWillPauseWhenDucked(true),然后创建并设置 OnAudioFocusChangeListener

4、setAcceptsDelayedFocusGain当焦点被其他应用锁定时,对音频焦点的请求可能会失败。此方法可实现延迟获取焦点,即在焦点可用时异步获取焦点。请注意,要使“延迟获取焦点”起作用,您还必须在音频请求中指定 AudioManager.OnAudioFocusChangeListener,因为您的应用必须收到回调才能知道自己获取了焦点

5、setOnAudioFocusChangeListener 一般来说,在原生请求中指定了 willPauseWhenDucked(true) 或 setAcceptsDelayedFocusGain(true) 时,才需要 OnAudioFocusChangeListener。

音频焦点的申请流程

AudioManager requestAudioFocus(AudioFocusRequest focusRequest) --> requestAudioFocus(AudioFocusRequest afr, AudioPolicy ap)

public int requestAudioFocus(@NonNull AudioFocusRequest afr, @Nullable AudioPolicy ap) {
	//将AudioFocusRequest 中listener 保存到一个ConcurrentHashMap中
    registerAudioFocusRequest(afr);
    //关于AudioPolicy ap 这个参数我们后面再说
	...
    //获取IAudioService
    final IAudioService service = getService();
 	...
 	//通过binder 调用AudioService的requestAudioFocus
    synchronized (mFocusRequestsLock) {
        try {
            // TODO status contains result and generation counter for ext policy
            status = service.requestAudioFocus(afr.getAudioAttributes(),
                    afr.getFocusGain(), mICallBack,
                    mAudioFocusDisp
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值