如何妥善处理音频中断的问题?

无论你开发的 app 有多少花里胡哨的功能,如果不能在用户接听电话时暂停音视频播放,用户依然不会买你的账。设备在播放音视频时会提示用户接听电话,如果用户挂断电话后发现自己错过了播客的最后 12 分钟,这该怪谁呢?当然该怪开发者。

因此,当失去音频焦点时请暂停播放音频焦点指的是设备在同一时间点只能播放一个音频,所以系统需要跟踪哪些 app 正在播放音频以及用户对什么感兴趣。音频焦点在设备中就像一个麦克风——谁掌握了音频焦点,谁就可以说话。但当其他人掌握音频焦点时,系统就会把你静音。

假设你的 app 正在推流音乐,大多数情况下,用户都希望在听音乐的过程中不被打断。

  • 如果用户收到来电提示并且接听电话,这时用户当然希望音乐能暂停播放。所以系统会将 app 静音,你应该注意到用户接听电话这个时间段里你短暂的失去了音频焦点(通话后会再次获得音频焦点!),所以要在用户通话的过程中暂停音乐。
  • 如果用户正在听导航指令,而且导航指令的音量可能会超过音乐,你可以暂时降低音量(“闪避功能”)或将音乐暂停直至导航指令结束。
  • 如果用户听完音乐后决定播放播客(打开其他 app)时,就会导致永久失去音频焦点,你的 app 应该停止工作。

音频焦点就是可以提醒你各种状况的麦克风!

伊恩·莱克(Ian Lake)在 BABBQ 的媒体播放演讲中对音频焦点进行了非常透彻的分析,大家一定要去看看。下文对如何应对音频焦点的变化进行了分析。

你可以用 AudioManager 来管理 app 的音频焦点。如果要播放音频,只需提出请求(完成后别忘了释放请求)。获得音频焦点授权后,就可以播放音频了,但是系统可能会暂时或永久收回音频焦点。因此,你需要一个 OnAudioFocusChangeListener 来跟踪音频焦点的状态并及时反应!代码如下:

AudioManager.OnAudioFocusChangeListener afChangeListener = 
    new AudioManager.OnAudioFocusChangeListener() {
  public void onAudioFocusChange(int focusChange) {
    if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
      // Pause playback because your Audio Focus was
      // temporarily stolen, but will be back soon.
      // i.e. for a phone call
    } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
      // Stop playback, because you lost the Audio Focus.
      // i.e. the user started some other playback app
      // Remember to unregister your controls/buttons here.
      // And release the kra — Audio Focus!
      // You’re done.
      am.abandonAudioFocus(afChangeListener);
    } else if (focusChange ==
        AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
      // Lower the volume, because something else is also
      // playing audio over you.
      // i.e. for notifications or navigation directions
      // Depending on your audio playback, you may prefer to
      // pause playback here instead. You do you.
    } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
      // Resume playback, because you hold the Audio Focus
      // again!
      // i.e. the phone call ended or the nav directions
      // are finished
      // If you implement ducking and lower the volume, be
      // sure to return it to normal here, as well.
    }
  }
};

随时复制、粘贴就能开始使用,或者将其转换为switch语句并在互联网上嘲笑我。只要你能正确使用音频焦点,哪种方式都是可以的!

欢迎使用上述代码!大家也可以把上述代码装换为 switch 语句,无论哪种方式,只要大家可以正确使用音频焦点就可以了!

使用旧的 Android 版本的开发人员需要使用 PhoneStateListener 作为通话过程中暂停播放的信号,但 Lollipop 完全不同。在Lollipop+ 设备中,手机的状态被合理的集成到了音频焦点状态中,你的 app 会在通话开始时收到 AUDIOFOCUS_LOSS_TRANSIENT,在通话结束时收到 AUDIOFOCUS_GAIN。现在只差一个 API!

值得一提的是,从 Android 6.0 开始,LISTEN_CALL_STATE 就不再需要 READ_PHONE_STATE 权限,这是一个进步, 但是其他几个电话选项需要,并且开发人员也不想冒着写出糟糕代码的风险,向用户请求电话许可,只为了能暂停音乐播放。

注意,之前的 Lollipop 设备上也要使用音频焦点——仅对手机状态做出反应,就可能会错过其他音频提示,例如导航指示或其他 app 启动的声音。app 应该为用户服务,而不是给用户徒添烦恼。

因此,请用正确的方式播放音频,用音频焦点为用户创建无缝体验,只有这样才能开发出更好的 App。

欢迎大家加入 Google+ 的讨论,也可以关注 Android开发模式合集以获取更多信息!
1_3TliuZh1EbdCBwiP428xpw


原文作者:Joanna Smith
原文链接:https://medium.com/google-developers/how-to-properly-handle-audio-interruptions-3a13540d18fa


推荐阅读
相关专栏
音视频杂谈
158 文章
本专栏仅用于分享音视频相关的技术文章,与其他开发者和声网 研发团队交流、分享行业前沿技术、资讯。发帖前,请参考「社区发帖指南」,方便您更好的展示所发表的文章和内容。