📜 ⬆️ ⬇️

Audiofocus - control access to the audio subsystem

This is a translation of the article Respecting Audio Focus by Kristan Uccello, Google Developer Relations

It 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 request
AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); int result = am.requestAudioFocus(mOnAudioFocusChangeListener, // Hint: the music stream. AudioManager.STREAM_MUSIC, // Request permanent focus. AudioManager.AUDIOFOCUS_GAIN); if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {  mState.audioFocusGranted = true; } else if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {  mState.audioFocusGranted = false; } 

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

Source: https://habr.com/ru/post/197068/


All Articles