Android assigns audio focus to one app at a time.
Beginning with Android 2.2 (API level 8), apps manage audio focus by calling
requestAudioFocus()
and abandonAudioFocus(). They also register an
AudioManager.OnAudioFocusChangeListener with both calls in order to receive callbacks and manage their own audio level.
Android O introduces a new class, AudioFocusRequest,
which contains information about the audio context and capabilities of your app.
The system uses AudioFocusRequest to help manage the gain and loss of audio
focus.
Major changes in the O Preview release
Requesting and abandoning audio focus
Beginning with Android O, the method requestAudioFocus() takes only one argument, an AudioFocusRequest. The method abandonAudioFocusRequest() replaces the method abandonAudioFocus(), and also takes an AudioFocusRequest as its argument. The same AudioFocusRequest instance should be used when requesting and abandoning focus.
Automatic ducking
Prior to Android O, when an app lost and regained audio focus while playing, it performed its own volume decrease and restoration in the onAudioFocusChange callback. Now, in Android O, when another application requests focus with AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK the system can duck and restore the volume without calling the app.
While automatic ducking is acceptable behavior for music and video playback applications, it isn't useful when playing spoken content (for instance, an audio book). In this case the application should pause instead.
If you want your app to pause when asked to duck rather than decrease its volume, create an OnAudioFocusChangeListener with
an onAudioFocusChange() callback method that implements the desired pause/resume behavior.
Call setOnAudioFocusChangeListener() to register the listener, and call
setWillPauseWhenDucked(true)
to tell the system to use your callback rather than perform automatic ducking.
Delayed focus gain
Sometimes the system cannot grant a request for audio focus because the focus is "locked" by another app (for instance, during a phone call). In this case requestAudioFocus() returns AUDIOFOCUS_REQUEST_FAILED. When this happens your application should not proceed with audio playback because it did not gain focus. Prior to Android O, there was no way for an app to wait for focus when the lock was lifted. An app would need to continually send requests until focus was granted.
Android O provides a method, setAcceptsDelayedFocusGain(true), that lets your app handle a request for focus asynchronously. With this flag set, a request made when the focus is locked returns AUDIOFOCUS_REQUEST_DELAYED. When the condition that locked the audio focus no longer exists (for instance, at the end of a phone call), the system grants the pending focus request and calls onAudioFocusChange() to notify your app.
In order to handle the delayed gain of focus, you must create an OnAudioFocusChangeListener with
an onAudioFocusChange() callback method that implements the desired behavior and register the listener by calling
setOnAudioFocusChangeListener().
AudioFocusRequest
The Android O requestAudioFocus() method takes a single object, AudioFocusRequest. Use an AudioFocusRequest.Builder to define the fields of the request. Since a focus request must always specify the type of the request, it is included in the constructor for the builder. Use the builder's methods to set the other fields of the request.
The FocusGain field is required, all the other fields are optional.
| Method | Notes |
|---|---|
setFocusGain()
|
This field is required in every request. It takes the same values as the durationHint used in the pre-Android O call to requestAudioFocus(): AUDIOFOCUS_GAIN, AUDIOFOCUS_GAIN_TRANSIENT, AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, or AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE.
|
setAudioAttributes()
|
AudioAttributes describes the use case for your app. The system looks at them when an app gains and loses audio focus.
Attributes supersede the notion of stream type. (In Android O, stream types for any operation other than volume controls are deprecated.) Use the same attributes in the focus request that you use in your audio player (as
shown in the example below).
Use an
If not specified, |
setWillPauseWhenDucked()
|
In Android O, when another application requests focus with AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
the application that has focus does not usually receive an onAudioFocusChange callback because the system can do the ducking by itself. When you need to pause playback rather than duck the volume, call setWillPauseWhenDucked(true) and create and set an OnAudioFocusChangeListener as described above, in automatic ducking.
|
setAcceptsDelayedFocusGain()
|
A request for audio focus can fail when the focus is locked by another app.
This method enables delayed focus gain: the ability
to asynchronously acquire focus when it becomes available.
Note that delayed focus gain only works if you also specify an OnAudioChangeFocusListener in the audio request, since your application needs to receive the callback in order to know that focus was granted. |
setOnAudioFocusChangeListener()
|
An OnAudioFocusChangeListener is only required if you also specify
WillPauseWhenDucked(true) or AcceptsDelayedFocusGain(true) in the request.
There are two methods for setting the listener: one with and one without a handler argument. The handler is the thread on which the listener runs. If you do not specify a handler, the handler associated with the main Looper is used. |
The following example shows how to use an AudioFocusRequest.Builder to build an AudioFocusRequest and request and abandon audio focus:
mAudioManager = (AudioManager) Context.getSystemService(Context.AUDIO_SERVICE);
mPlaybackAttributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_GAME)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build();
mFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setAudioAttributes(mPlaybackAttributes)
.setAcceptsDelayedFocusGain(true)
.setOnAudioFocusChangeListener(mMyFocusListener, mMyHandler)
.build();
mMediaPlayer = new MediaPlayer();
final Object mFocusLock = new Object();
boolean mPlaybackDelayed = false;
boolean mPlaybackNowAuthorized = false;
// ...
int res = mAudioManager.requestAudioFocus(mFocusRequest);
synchronized(mFocusLock) {
if (res == AUDIOFOCUS_REQUEST_FAILED) {
mPlaybackNowAuthorized = false;
} else if (res == AUDIOFOCUS_REQUEST_GRANTED) {
mPlaybackNowAuthorized = true;
playbackNow();
} else if (res == AUDIOFOCUS_REQUEST_DELAYED) {
mPlaybackDelayed = true;
mPlaybackNowAuthorized = false;
}
}
// ...
@Override
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
if (mPlaybackDelayed || mResumeOnFocusGain) {
synchronized(mFocusLock) {
mPlaybackDelayed = false;
mResumeOnFocusGain = false;
}
playbackNow();
}
break;
case AudioManager.AUDIOFOCUS_LOSS:
synchronized(mFocusLock) {
mResumeOnFocusGain = false;
mPlaybackDelayed = false;
}
pausePlayback();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
synchronized(mFocusLock) {
mResumeOnFocusGain = true;
mPlaybackDelayed = false;
}
pausePlayback();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
// ... pausing or ducking depends on your application
break;
}
}
}