Scott Main | 604e4ed | 2011-12-13 18:24:34 -0800 | [diff] [blame] | 1 | page.title=Managing Audio Focus |
Scott Main | 580f014 | 2011-12-15 16:47:26 -0800 | [diff] [blame] | 2 | parent.title=Managing Audio Playback |
Scott Main | 604e4ed | 2011-12-13 18:24:34 -0800 | [diff] [blame] | 3 | parent.link=index.html |
| 4 | |
| 5 | trainingnavtop=true |
| 6 | previous.title=Controlling Your App's Volume and Playback |
| 7 | previous.link=volume-playback.html |
| 8 | next.title=Dealing with Audio Output Hardware |
| 9 | next.link=audio-output.html |
| 10 | |
| 11 | @jd:body |
| 12 | |
| 13 | |
| 14 | <div id="tb-wrapper"> |
| 15 | <div id="tb"> |
| 16 | |
| 17 | <h2>This lesson teaches you to</h2> |
| 18 | <ol> |
| 19 | <li><a href="#RequestFocus">Request the Audio Focus</a></li> |
| 20 | <li><a href="#HandleFocusLoss">Handle the Loss of Audio Focus</a></li> |
| 21 | <li><a href="#DUCK">Duck!</a></li> |
| 22 | </ol> |
| 23 | |
| 24 | |
| 25 | <h2>You should also read</h2> |
| 26 | <ul> |
| 27 | <li><a href="{@docRoot}guide/topics/media/mediaplayer.html">Media Playback</a></li> |
| 28 | </ul> |
| 29 | |
| 30 | </div> |
| 31 | </div> |
| 32 | |
| 33 | |
| 34 | <p>With multiple apps potentially playing audio it's important to think about how they should |
| 35 | interact. To avoid every music app playing at the same time, Android uses audio focus to moderate |
| 36 | audio playback—only apps that hold the audio focus should play audio.</p> |
| 37 | |
| 38 | <p>Before your app starts playing audio it should request—and receive—the audio focus. |
| 39 | Likewise, it should know how to listen for a loss of audio focus and respond appropriately when that |
| 40 | happens.</p> |
| 41 | |
| 42 | |
| 43 | <h2 id="RequestFocus">Request the Audio Focus</h2> |
| 44 | |
| 45 | <p>Before your app starts playing any audio, it should hold the audio focus for the stream |
| 46 | it will be using. This is done with a call to {@link android.media.AudioManager#requestAudioFocus |
| 47 | requestAudioFocus()} which returns |
| 48 | {@link android.media.AudioManager#AUDIOFOCUS_REQUEST_GRANTED} if your request is successful.</p> |
| 49 | |
| 50 | <p>You must specify which stream you're using and whether you expect to require transient or |
| 51 | permanent audio focus. Request transient focus when you expect to play audio for only a short time |
| 52 | (for example when playing navigation instructions). Request permanent audio focus when you |
| 53 | plan to play audio for the foreseeable future (for example, when playing music).</p> |
| 54 | |
| 55 | <p>The following snippet requests permanent audio focus on the music audio stream. You should |
| 56 | request the audio focus immediately before you begin playback, such as when the user presses |
| 57 | play or the background music for the next game level begins.</p> |
| 58 | |
| 59 | <pre> |
| 60 | AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE); |
| 61 | ... |
| 62 | |
| 63 | // Request audio focus for playback |
| 64 | int result = am.requestAudioFocus(afChangeListener, |
| 65 | // Use the music stream. |
| 66 | AudioManager.STREAM_MUSIC, |
| 67 | // Request permanent focus. |
| 68 | AudioManager.AUDIOFOCUS_GAIN); |
| 69 | |
| 70 | if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { |
| 71 | am.unregisterMediaButtonEventReceiver(RemoteControlReceiver); |
| 72 | // Start playback. |
| 73 | } |
| 74 | </pre> |
| 75 | |
| 76 | <p>Once you've finished playback be sure to call {@link |
| 77 | android.media.AudioManager#abandonAudioFocus abandonAudioFocus()}. This notifies |
| 78 | the system that you no longer require focus and unregisters the associated {@link |
| 79 | android.media.AudioManager.OnAudioFocusChangeListener}. In the case of abandoning transient focus, |
| 80 | this allows any interupted app to continue playback.</p> |
| 81 | |
| 82 | <pre> |
| 83 | // Abandon audio focus when playback complete |
| 84 | am.abandonAudioFocus(afChangeListener); |
| 85 | </pre> |
| 86 | |
| 87 | <p>When requesting transient audio focus you have an additional option: whether or not you want to |
| 88 | enable "ducking." Normally, when a well-behaved audio app loses audio focus it immediately |
| 89 | silences its playback. By requesting a transient audio focus that allows ducking you tell other |
| 90 | audio apps that it’s acceptable for them to keep playing, provided they lower their volume until the |
| 91 | focus returns to them.</p> |
| 92 | |
| 93 | <pre> |
| 94 | // Request audio focus for playback |
| 95 | int result = am.requestAudioFocus(afChangeListener, |
| 96 | // Use the music stream. |
| 97 | AudioManager.STREAM_MUSIC, |
| 98 | // Request permanent focus. |
| 99 | AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK); |
| 100 | |
| 101 | if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { |
| 102 | // Start playback. |
| 103 | } |
| 104 | </pre> |
| 105 | |
| 106 | <p>Ducking is particularly suitable for apps that use the audio stream intermittently, such as for |
| 107 | audible driving directions.</p> |
| 108 | |
| 109 | <p>Whenever another app requests audio focus as described above, its choice between permanent and |
| 110 | transient (with or without support for ducking) audio focus is received by the listener you |
| 111 | registered when requesting focus.</p> |
| 112 | |
| 113 | |
| 114 | <h2 id="HandleFocusLoss">Handle the Loss of Audio Focus</h2> |
| 115 | |
| 116 | <p>If your app can request audio focus, it follows that it will in turn lose that focus when another |
| 117 | app requests it. How your app responds to a loss of audio focus depends on the manner of that |
| 118 | loss.</p> |
| 119 | |
| 120 | <p>The {@link android.media.AudioManager.OnAudioFocusChangeListener#onAudioFocusChange |
| 121 | onAudioFocusChange()} callback method of they audio focus change listener you registered when |
| 122 | requesting audio focus receives a parameter that describes the focus change event. Specifically, |
| 123 | the possible focus loss events mirror the focus request types from the previous |
| 124 | section—permanent loss, transient loss, and transient with ducking permitted.</p> |
| 125 | |
| 126 | <p>Generally speaking, a transient (temporary) loss of audio focus should result in your app |
| 127 | silencing it’s audio stream, but otherwise maintaining the same state. You should continue to |
| 128 | monitor changes in audio focus and be prepared to resume playback where it was paused once you’ve |
| 129 | regained the focus.</p> |
| 130 | |
| 131 | <p>If the audio focus loss is permanent, it’s assumed that another application is now being used to |
| 132 | listen to audio and your app should effectively end itself. In practical terms, that means stopping |
| 133 | playback, removing media button listeners—allowing the new audio player to exclusively handle |
| 134 | those events—and abandoning your audio focus. At that point, you would expect a user action |
| 135 | (pressing play in your app) to be required before you resume playing audio.</p> |
| 136 | |
| 137 | <p>In the following code snippet, we pause the playback or our media player object if the audio |
| 138 | loss is transien and resume it when we have regained the focus. If the loss is permanent, it |
| 139 | unregisters our media button event receiver and stops monitoring audio focus changes.<p> |
| 140 | |
| 141 | <pre> |
| 142 | OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() { |
| 143 | public void onAudioFocusChange(int focusChange) { |
| 144 | if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT |
| 145 | // Pause playback |
| 146 | } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) { |
| 147 | // Resume playback |
| 148 | } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) { |
| 149 | am.unregisterMediaButtonEventReceiver(RemoteControlReceiver); |
| 150 | am.abandonAudioFocus(afChangeListener); |
| 151 | // Stop playback |
| 152 | } |
| 153 | } |
| 154 | }; |
| 155 | </pre> |
| 156 | |
| 157 | <p>In the case of a transient loss of audio focus where ducking is permitted, rather than pausing |
| 158 | playback, you can "duck" instead.</p> |
| 159 | |
| 160 | |
| 161 | <h2 id="DUCK">Duck!</h2> |
| 162 | |
| 163 | <p>Ducking is the process of lowering your audio stream output volume to make transient audio from |
| 164 | another app easier to hear without totally disrupting the audio from your own application.</p> |
| 165 | |
| 166 | <p>In the following code snippet lowers the volume on our media player object when we temporarily |
| 167 | lose focus, then returns it to its previous level when we regain focus.</p> |
| 168 | |
| 169 | <pre> |
| 170 | OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() { |
| 171 | public void onAudioFocusChange(int focusChange) { |
| 172 | if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK |
| 173 | // Lower the volume |
| 174 | } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) { |
| 175 | // Raise it back to normal |
| 176 | } |
| 177 | } |
| 178 | }; |
| 179 | </pre> |
| 180 | |
| 181 | <p>A loss of audio focus is the most important broadcast to react to, but not the only one. The |
| 182 | system broadcasts a number of intents to alert you to changes in user’s audio experience. |
| 183 | The next lesson demonstrates how to monitor them to improve the user’s overall experience.</p> |