本文译自:http://developer.android.com/training/managing-audio/audio-focus.html

考虑如何跟多个潜在的音频播放程序交互是至关重要的。要避免每个音乐播放器同时播放,Android使用音频焦点(Audio Focus)来控制音频播放---只有拥有音频焦点的应用程序才能播放音频。

在你的应用程序开始播放音频之前,它应该请求、接收音频焦点。而且,它还应该知道如何监听音频焦点的丢失,以及在音频焦点丢失时,如何做出正确的响应。

请求音频焦点

在应用程序开始播放任何音频之前,它应该拥有它将使用的音频流的音频焦点。通过调用requestAudioFocus()方法来获取音频焦点,如果请求成功,该方法会返回AUDIOFOCUS_REQUEST_GRANTED。

你必须指定你所要使用的音频流,以及要求的音频焦点时临时性还是持久性的。当你期望只播放很短的音频是,可请求临时音频焦点(例如播放导航说明的时候)。在播放具有预见性的音频时,可请求持久性的音频焦点(例如,播放音乐的时候)。

下面的代码片段在音乐音频流上请求了持久性的音频焦点。在开始播放之前,你应该立即请求音频焦点,如用户按下播放按钮的时候或下个游戏级别的背景音乐开始之前。

AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE); 
... 
  
 // Request audio focus for playback 
 int result = am.requestAudioFocus(afChangeListener, 
                                  // Use the music stream. 
                                  AudioManager.STREAM_MUSIC, 
                                  // Request permanent focus. 
                                  AudioManager.AUDIOFOCUS_GAIN); 
     
 if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { 
     am.unregisterMediaButtonEventReceiver(RemoteControlReceiver); 
     // Start playback. 
 }

完成播放后,一定要确保调用abandonAudioFocus()方法。这个方法会通知系统你不再需要音频焦点了,并且会解除与AudioManager.OnAudioFocusChangeListener关联的注册。放弃临时性音频焦点的情况下,就会允许任何被中断的应用应用程序继续播放。

// Abandon audio focus when playback complete     
am.abandonAudioFocus(afChangeListener);

在请求临时性的音频焦点时会有另外一个选项:是否启用“回避”。通常,良好的音频应用程序在丢失音频焦点时立即关闭它的播放。通过请求临时性的允许回避的音频焦点,会告诉其他可访问的音频应用程序保持播放状态,但他们的音量会被降低,直到音频焦点返回给它们。

// Request audio focus for playback 
int result = am.requestAudioFocus(afChangeListener, 
                              // Use the music stream. 
                              AudioManager.STREAM_MUSIC, 
                              // Request permanent focus. 
                              AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK); 
     
 if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { 
     // Start playback. 
 }

“回避”特别适用于那些间歇性适用音频流的应用程序,如语音驾驶导航。

无论应用程序什么时候请求上述介绍的那种类型的音频焦点,都是通过注册监听器来接收持久性或临时性的音频焦点(不管支不支持回避)。

处理音频焦点的丢失

如果你的应用程序可以请求音频焦点,那么在另一个应用程序请求该焦点时,它又会丢失焦点。你的应用程序如何响应音频焦点的丢失依赖与对丢失焦点的管理。

你注册的音频焦点变化监听器的onAudioFocusChange()回调方法会接收一个描述焦点变化事件的参数。具体来说,可能的焦点丢失事件会对应到前面提到的请求焦点的类型---持久性丢失、临时性丢失,以及带有允许回避的临时性丢失。

一般来说,音频焦点的临时性丢失会导致你的应用程序让它的音频流保持沉默状态,但在其他方面却保持相同的状态。你应该继续监视音频焦点的变化,并准备在重新获取焦点时恢复被暂停的音频流播放。

如果丢失的音频焦点是持久性的,那么它会假设正在使用另一个应用程序来听音频,并且你应用程序应该被有效的结束。在实际的应用中,这意味着停止播放、删除媒体按钮监听器---允许新的音频播放器来专门处理这些事件---并且放弃你的音频焦点。这时在恢复播放音频之前,你会期望用户执行必要的操作(如按下应用中的播放按钮)。

在下面的代码片段中,如果丢失的是临时性的音频焦点,我们会暂停播放或媒体播放器对象,并且会在重新获取焦点时再恢复它。如果丢失的是持久性的音频焦点,它会解除我们的媒体按钮事件接收的注册,并且停止对音频焦点变化的监听。

OnAudioFocusChangeListener afChangeListener =newOnAudioFocusChangeListener(){ 
    public void onAudioFocusChange(int focusChange) { 
         if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT 
             // Pause playback 
         } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) { 
             // Resume playback  
         } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) { 
             am.unregisterMediaButtonEventReceiver(RemoteControlReceiver); 
             am.abandonAudioFocus(afChangeListener); 
             // Stop playback 
         } 
     } 
 };

在音频焦点临时性丢失,且允许回避的情况下,你可以用“回避”来代替暂停播放。

回避

回避是一种降低你的临时性音频流输出音量的处理,从而让另一个应用程序的音频更容易听到,而不至于与你的应用程序的音频流混淆。

下面的代码片段会在临时性丢失焦点时降低媒体播放对象的音量,然后在重新获取焦点时在返回到之前的级别。

OnAudioFocusChangeListener afChangeListener =newOnAudioFocusChangeListener(){ 
    public void onAudioFocusChange(int focusChange) { 
         if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) { 
             // Lower the volume 
         } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) { 
             // Raise it back to normal 
         } 
     } 
 };

音频焦点的丢失时最重要的广播,但却不是唯一的。系统会广播很多Intent来提醒你改变用户的音频体验。下节课会演示如何监视这些广播来改善用户的整体体验。