浅析Android音频焦点

前言

最近线上用户反馈了一些语音唤醒功能无法使用的问题(即无法通过语音来和应用交互),因此专门梳理了Android应用对音频焦点的处理逻辑并提出一些优化思路,下文是一次总结整理。

音频焦点出现的缘由、作用

为什么会有音频焦点的出现?两个或两个以上的 Android 应用可同时向同一输出流播放音频,系统会将所有音频流混合在一起。为了避免所有音乐应用同时播放,Android 引入了“音频焦点”的概念,一次只能有一个应用获得音频焦点。当应用需要输出音频时,它需要请求获得音频焦点,获得焦点后,就可以播放声音了。

不过,Android系统虽然可以对音频焦点进行分配,但不能强制让上一个占有音频焦点的应用程序放弃使用音频。即应用程序A正在前台使用语音唤醒功能的时候,应用B拨打了一个语音电话过来,这时Android系统会把音频焦点分配给应用B,但应用A的语音唤醒功能并没有被剥夺、仍然可以使用。那么应用B的语音电话功能就会受到影响。

这时就需要约定一个规则,当应用程序失去音频焦点的时候,就需要暂停使用音频或降低音频音量,把音频通道让给占有音频焦点的应用程序使用。如果应用违背上述规则,不释放音频通道,可能会引发大量客诉、流失掉很多用户。

音频焦点的使用核心

Android应用通过调用 requestAudioFocus()(请求音频焦点) 和 abandonAudioFocus()(放弃音频焦点) 来管理音频焦点。应用还必须为这两个方法注册监听器 AudioManager.OnAudioFocusChangeListener ,以便接收回调并管理自己的音量。

音频焦点具体的代码使用

AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);

    

    AudioManager.OnAudioFocusChangeListener afChangeListener =

      new AudioManager.OnAudioFocusChangeListener() {

        public void onAudioFocusChange(int focusChange) {

          if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {

            // 永久失去音频焦点

            // 立即停止播放

          }

          else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {

            // 暂时性失去焦点

            // 播放暂停

          } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {

            // 暂时性失去焦点

            // 声音变低,继续播放

          } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {

            // 获取到音频焦点

            // 声音恢复到正常音量,开始播放

          }

        }

      };

    int result = audioManager.requestAudioFocus(afChangeListener,

                                 // 使用音频流

                                 AudioManager.STREAM_MUSIC,

                                 AudioManager.AUDIOFOCUS_GAIN);

    if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {

        // 音频焦点请求成功,执行相应逻辑

    } else {

        // 音频焦点请求失败,执行相应逻辑

    }

播放完成后,放弃音频焦点:

audioManager.abandonAudioFocus(afChangeListener);

使用中注意的点:

① 为 requestAudioFocus 方法传入的第3个参数:

  • 如果计划在将来一段时间内播放音频,并且希望前一个持有音频焦点的应用停止播放,则应该请求永久性的音频焦点 (AUDIOFOCUS_GAIN)。
  • 如果只希望在短时间内播放音频,并且希望前一个持有音频焦点的应用暂停播放,则应该请求暂时性的焦点 (AUDIOFOCUS_GAIN_TRANSIENT)。
  • 如果只希望在短时间内播放音频,并允许前一个持有焦点的应用在降低音量的情况下继续播放,则应该请求“降低音量”的暂时性焦点(AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK) 。这两个音频会混合到音频流中同时输出。

② AudioManager.OnAudioFocusChangeListener回调回来的音频焦点类型,如下:

  • AUDIOFOCUS_GAIN:获取得到音频焦点。
  • AUDIOFOCUS_LOSS:永久性失去音频焦点,后续不会再收到 AUDIOFOCUS_GAIN 回调。应用应立即暂停播放,此时其他应用会播放音频。
  • AUDIOFOCUS_LOSS_TRANSIENT:暂时性失去音频焦点,应用应暂停播放。当抢占焦点的应用放弃焦点时、自己的应用可以收到 AUDIOFOCUS_GAIN 回调。
  • AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:暂时性失去音频焦点,应用应降低音量。当抢占焦点的应用放弃焦点时、自己的应用可以收到 AUDIOFOCUS_GAIN 回调。


开发中遇到的问题 & 解决思路

大多数情况下,按照上述流程使用音频焦点是没有问题的。但线上有部分用户反馈无法使用语音唤醒功能(即无法通过语音来和应用交互),经过排查,发现有如下可优化的地方:

  • 问题1:不是所有应用都会将音频焦点释放,有的应用也会不释放,比如手机系统的录音机。这看似违背最上面说的音频焦点管理规则,但录音机是有它特殊性的,不管它位于前台还是后台,都应该拥有录音的功能。这就会导致只要不杀死录音机应用进程,它就会一直占用音频焦点,导致其他应用无法使用音频焦点。或者是其他应用把原本属于本应用的音频焦点抢占了,也会导致本应用无法使用音频焦点,比如某些应用内的语音通话功能就会抢占音频焦点。

    处理策略:当AudioManager.OnAudioFocusChangeListener监听器监听到音频焦点已失去时(AUDIOFOCUS_LOSS、AUDIOFOCUS_LOSS_TRANSIENT),就主动通过弹窗或toast提示用户【关闭其他应用或重启手机】,以期望达到让其他应用释放音频焦点的目的。

    问题2:正常来说,当B应用把音频焦点释放后,A应用是可以重新获取到音频焦点的回调(AUDIOFOCUS_GAIN),但某些Android手机可能会有系统bug,即不会主动通知A应用音频焦点已经回来了,A应用无法拿到AUDIOFOCUS_GAIN这个回调,A应用也就无法再恢复播放音频。
    处理策略:增加音频功能的调用时机。比如在切页或者做其他操作的时候,主动尝试使用语音唤醒的音频功能,而不是通过AudioManager.OnAudioFocusChangeListener监听器被动地等待回调。当主动尝试使用语音唤醒成功时,说明音频焦点已经回来了。

    以上是Android音频焦点的使用介绍以及所遇到问题的分析和处理。欢迎关注公众号度熊君,一起分享交流
  • 原文 浅析Android音频焦点 - 掘金

★文末名片可以免费领取音视频开发学习资料,内容包括(FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)以及音视频学习路线图等等。

见下方!↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值