不管你的app有多出色,我们都可以打赌:如果你不在用户接听电话时暂停音频播放,他们就会 讨厌 你。当用户接到无聊的电话时,他们就会挂断电话,然后发现自己错过了播客的最后12分钟,具体怎么回事他们也不知道。但最后他们还是会把责任归咎到你的身上。

因此, 当你失去音频焦点时 , 请暂停音频播放 。音频焦点 指的是一次只能播放一件事,这意味着系统需要一个方法来跟踪哪些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语句并在互联网上嘲笑我。只要你能正确使用音频焦点,哪种方式都是可以的!

尽管旧的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开发模式合集以获取更多信息!

作者 Joanna Smith

原文链接 How to _properly_ handle audio interruptions