This is a translation of the article Respecting Audio Focus by Kristan Uccello, Google Developer RelationsIt is considered rude to interrupt during the report, it shows disrespect for the speaker and annoys the audience. If your application does not take into account the rules of working with audiofocus, it means that it does not respect other applications and annoys the user.
If you have never heard of audiofocus, you should pay attention to the documentation
Android developer training material .
When multiple applications can play audio, it is important to think about how they will interact. To avoid a situation when all players play simultaneously, Android uses the concept of audiofocus to control the playback of sounds: your application should play audio only when it has received audiofocus. This article describes a few tips on how to correctly and best for the user to handle changes in the state of audiofocus.
Request audiofocus
No need to be greedy and request audio focus right at the moment of launching the application; it’s better to wait until the application starts doing something with the audio stream. When you receive audio focus through the AudioManager service, you can use the constants
AUDIOFOCUS_GAIN * to indicate the required focus mode.
')
Sample focus requestAudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); int result = am.requestAudioFocus(mOnAudioFocusChangeListener,
In the example, we request a permanent audio focus. Instead, we could request a temporary (
AUDIOFOCUS_GAIN_TRANSIENT ) focus, which is suitable for playing sounds of up to 45 seconds.
The application can also use the “cracking” mode (
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK ) for situations when sharing the audio subsystem with other applications (for example, for the phrase “burn more” in the fitness application, expecting that the background music will not be interrupted). An application requesting focus in the “grunt” mode should not use the audio subsystem for more than 15 seconds in a row.
We process changes in the state of audiofocus
To handle audiofocus state change events, the application must create an
OnAudioFocusChangeListener instance. In this handler, you need to handle the
AUDIOFOCUS_GAIN * and
AUDIOFOCUS_LOSS * events. It is worth noting that the AUDIOFOCUS_GAIN event has several features described in the second example.
Event handling example mOnAudioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() { @Override public void onAudioFocusChange(int focusChange) { switch (focusChange) { case AudioManager.AUDIOFOCUS_GAIN: mState.audioFocusGranted = true; if(mState.released) { initializeMediaPlayer(); } switch(mState.lastKnownAudioFocusState) { case UNKNOWN: if(mState.state == PlayState.PLAY && !mPlayer.isPlaying()) { mPlayer.start(); } break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: if(mState.wasPlayingWhenTransientLoss) { mPlayer.start(); } break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: restoreVolume(); break; } break; case AudioManager.AUDIOFOCUS_LOSS: mState.userInitiatedState = false; mState.audioFocusGranted = false; teardown(); break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: mState.userInitiatedState = false; mState.audioFocusGranted = false; mState.wasPlayingWhenTransientLoss = mPlayer.isPlaying(); mPlayer.pause(); break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: mState.userInitiatedState = false; mState.audioFocusGranted = false; lowerVolume(); break; } mState.lastKnownAudioFocusState = focusChange; } };
The constant AUDIOFOCUS_GAIN is used in the code in two different roles. First, to get audio focus as in example 1. At that, the
OnAudioFocusChangeListener event does not occur, that is, if the
audio focus is successfully requested (and received), the handler will NOT receive the corresponding AUDIOFOCUS_GAIN event.
AUDIOFOCUS_GAIN is also used in the
OnAudioFocusChangeListener implementation as an event variant. As stated earlier, the AUDIOFOCUS_GAIN event is not raised when an audio focus is requested. On the contrary, it can occur only after the corresponding event
AUDIOFOCUS_LOSS * . AUDIOFOCUS_GAIN is the only constant that is used in both situations.
There are four situations that need to be taken into account in the event handler for the change in state of the audio focus. When an application receives an AUDIOFOCUS_LOSS event, it usually means that it will not receive the audio focus back. In this case, the application must release the resources associated with the audio subsystem and stop playback. As an example, imagine that a user listens to music through your application, and then starts the game, which takes the audio focus away from the audio player. It is impossible to predict how long the user will close the game. Most likely, it will go to the main screen (leaving the game in the background) and launch another application. Or he will return to the audio player, resuming his work, which will require a new request for audiofocus in onResume.
However, there is another case worthy of discussion. There is a difference between losing the audio focus permanently (as in the example above) or temporarily. When an application receives an
AUDIOFOCUS_LOSS_TRANSIENT event, the application is expected to suspend audio usage until it receives an AUDIOFOCUS_GAIN event. When the AUDIOFOCUS_LOSS_TRANSIENT event occurs, the application must remember that the focus loss is temporary in order to figure out what behavior is correct when you return the focus. (see example 2).
Sometimes an application loses audiofocus (i.e., receives AUDIOFOCUS_LOSS), and the interrupting application terminates, or loses audiofocus in some other way. In this situation, the last application that had audiofocus can receive the event AUDIOFOCUS_GAIN.
In the subsequent AUDIOFOCUS_GAIN event, the application should understand whether it received audio focus after a temporary loss and should simply resume playback, or recover and adjust playback after a complete loss of focus.
If an application uses audio only for a short time (no more than 45 seconds), it should request the audio focus in
AUDIOFOCUS_GAIN_TRANSIENT mode and release it immediately after the end of playback or recording of sound. The audio focus in the system is treated as a stack: the focus is received by the application that owned it last.
When audiofocus is received, it's time to create a MediaPlayer or MediaRecorder and reserve resources. Also, when an application receives AUDIOFOCUS_LOSS, it is good practice to release all the reserved resources. There are three options for obtaining audiofocus, corresponding to different options for losing focus. It would be nice to explicitly handle all the options for losing focus in the
OnAudioFocusChangeListener handler.
Table 1 . The meaning of reception constants and loss of audiofocus
GAIN
| Loss
|
AUDIOFOCUS_GAIN
| AUDIOFOCUS_LOSS
|
AUDIOFOCUS_GAIN_TRANSIENT (*)
| AUDIOFOCUS_LOSS_TRANSIENT
|
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK (*)
| AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
|
Note: the constant is used in two places. When an audio focus is requested, it is transmitted as an AudioManager hint; it is also used as a variant of the event in
OnAudioFocusChangeListener . Focus acquisition constants marked with (*) are used only when requesting audiofocus. The focus loss constants are used only in the OnAudioFocusChangeListener handler.
Table 2 . Types of audio streams.
Type of
| Description
|
STREAM_ALARM
| Alarm clock
|
STREAM_DTMF
| Tone dialing
|
STREAM_MUSIC
| Multimedia playback (music, podcasts, video)
|
STREAM_NOTIFICATION
| Notifications
|
STREAM_RING
| Phone call
|
STREAM_SYSTEM
| System sounds
|
The application requests audio focus from
AudioManager (as in the example at the link at the end of the article). The parameters are an optional handler, a hint with the type of audio channel (table 2) and the type of audio focus from table 1. Any audio initialization should be done only if the system has allowed receiving audio focus (AudioManager.
AUDIOFOCUS_REQUEST_GRANTED ).
Note: If a telephone conversation occurs, the system will not allow audio focus (
AUDIOFOCUS_REQUEST_FAILED ) and will not send the AUDIOFOCUS_GAIN event to the application after the end of the call.
A brief description of the application's reaction to the OnAudioFocusChange () events is described in Table 3.
In case of loss of audiofocus, it is necessary to be sure that the focus is lost completely. If an application receives an
AUDIOFOCUS_LOSS_TRANSIENT or
AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK event , it can hold reserved resources (do not call release ()), because most likely a new change in audiofocus event will occur. It is necessary to save information about the temporary loss of focus in any flag or by moving to a separate vertex of the state graph.
If the application requested a constant audio focus in AUDIOFOCUS_GAIN mode and received an AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK event, the appropriate reaction would be to turn the volume down (not forgetting to keep the old volume value) and then return the volume when receiving the AUDIOFOCUS_GAIN event (see picture).
Table 3 . Application reaction when the state of audiofocus changes.
Focus Change Type
| Reaction
|
AUDIOFOCUS_GAIN
| Receive event after a loss of focus event : Resume media playback if the state of the application does not conflict with this. For example, a user paused before a focus loss event.
|
AUDIOFOCUS_LOSS
| Stop playback, release resources
|
AUDIOFOCUS_LOSS_TRANSIENT
| Pause playback and save the flag stating that the loss of focus is temporary, so that when processing AUDIOFOCUS_GAIN you can resume playback if necessary. Do not release resources.
|
AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
| Make the volume quieter or pause playback, not forgetting to track the status as in the case of AUDIOFOCUS_LOSS_TRANSIENT. Do not release resources.
|
Conclusion and what to read
Being an “exemplary citizen” in the camp of audio applications on android devices means respecting the rules of working with audiofocus and correctly handling all situations. Try to get your application to act in a reasonable manner and not give the user any unpleasant surprises. There is much more interesting to tell about the audio subsystem of the androyd, from the links below you can read more about it.
Source codes from the article are available at the link:
https://android.googlesource.com/platform/development/+/master/samples/RandomMusicPlayer