Skip to content

Most visited

Recently visited

navigation

Handling Changes in Audio Output

Besides responding to your UI controls and media buttons, an audio app should also react to other Android events that can affect its sound. This page describes how to handle three cases:

Honor the volume controls

When a user presses a volume key in a game or music app the volume should change, even if the player is paused between songs or there’s no music for the current game location.

Android uses separate audio streams for playing music, alarms, notifications, the incoming call ringer, system sounds, in-call volume, and DTMF tones. This allows users to control the volume of each stream independently.

By default, pressing the volume control modifies the volume of the active audio stream. If your app isn't currently playing anything, hitting the volume keys adjusts the ringer volume.

Unless your app is a replacement alarm clock, you probably play audio using the STREAM_MUSIC stream.

To ensure that volume controls adjust the correct stream, you should call setVolumeControlStream() passing in AudioManager.STREAM_MUSIC.

setVolumeControlStream(AudioManager.STREAM_MUSIC);

Make this call early in your app’s lifecycle, typically from the onCreate() method of the activity or fragment that controls your media. This connects the volume controls to STREAM_MUSIC whenever the target activity or fragment is visible.

Don't be noisy

Users have a number of alternatives when it comes to enjoying the audio from their Android devices. Most devices have a built-in speaker, headphone jacks for wired headsets, and many also feature Bluetooth connectivity and support for A2DP audio.

When a headset is unplugged or a Bluetooth device disconnected, the audio stream automatically reroutes to the built-in speaker. If you listen to music at a high volume, this can be a noisy surprise.

Luckily the system broadcasts an ACTION_AUDIO_BECOMING_NOISY intent when this happens. You should create a BroadcastReceiver that listens for this intent whenever you’re playing audio. In the case of music players, users typically expect the playback to be paused. For gaming apps, you may choose to significantly lower the volume instead. Your receiver should look like this:

private class BecomingNoisyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
      if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
          // Pause the playback
      }
    }
}

Register the receiver when you begin playback, and unregister it when you stop. If you design your app as we describe in this guide, these calls should appear in the onPlay() and onStop() media session callbacks.

private IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
private BecomingNoisyReceiver myNoisyAudioStreamReceiver = new BecomingNoisyReceiver();

MediaSessionCompat.Callback callback = new
MediaSessionCompat.Callback() {
  @Override
  public void onPlay() {
    registerReceiver(myNoisyAudioStreamReceiver, intentFilter);
  }

  @Override
  public void onStop() {
    unregisterReceiver(myNoisyAudioStreamReceiver);
  }
}

Share the audio focus

Two or more Android apps can play audio to the same output stream simultaneously. The system mixes everything together. While this is technically impressive, it can be very aggravating to a user. To avoid every music app playing at the same time, Android introduces the idea of audio focus. Only one app can hold audio focus at a time.

When your application needs to output audio, it should request audio focus. When it has focus, it can play sound. However, after you acquire audio focus you may not be able to keep it until you’re done playing. Another app can request focus, which preempts your hold on audio focus. If that happens your app should pause playing or lower its volume to allow the new audio source to be heard.

A well-behaved audio app should manage audio focus according to these guidelines:

Audio Focus is cooperative. Applications are encouraged to comply with the audio focus guidelines, but the system does not enforce the rules. If an application wants to continue to play loudly even after losing audio focus, nothing can prevent that. This is a bad experience and there's a good chance that users will uninstall an app that misbehaves in this way.

Acquiring and releasing audio focus

Your app should request the audio focus for the stream it uses immediately before playback begins. For example, when the user presses play or the background music for the next game level starts.

Before proceeding to play, call requestAudioFocus() and verify that it returned AUDIOFOCUS_REQUEST_GRANTED. The call to requestAudioFocus() should be made in the onPlay() callback of a media session if you design your app as we describe in this guide.

When you request audio focus you must specify a duration hint, which may be honored by another app that is currently holding focus and playing:

The requestAudioFocus() method also requires an AudioManager.OnAudioFocusChangeListener. This listener should be created in the same activity or service that owns your media session. It implements the callback onAudioFocusChange() that your app will receive when some other app acquires or abandons audio focus.

The following snippet requests permanent audio focus on the stream STREAM_MUSIC and registers an OnAudioFocusChangeListener to handle subsequent changes in audio focus. ( The change listener is discussed in the next section.)

AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
AudioManager.OnAudioFocusChangeListener afChangeListener;

...
// 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) {
    // Start playback
}

When you finish playback, call abandonAudioFocus().

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

This notifies the system that you no longer require focus and unregisters the associated OnAudioFocusChangeListener. If you requested transient focus, this will notify an app that paused or ducked that it may continue playing or restore its volume.

Responding to audio focus change

When an app acquires audio focus it must be able to release it when another app requests audio focus for itself. When this happens your app receives a call to the onAudioFocusChange() method in the AudioFocusChangeListener that you specified when the app called requestAudioFocus().

The focusChange parameter passed to onAudioFocusChange() indicates the kind of change that's happening. It corresponds to the duration hint used by the app that's aquiring focus. Your app should respond appropriately.

Transient loss of focus
If the focus change is transient (AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK or AUDIOFOCUS_LOSS_TRANSIENT), your app should duck or pause playing but otherwise maintain the same state.

During a transient loss of audio focus you should continue to monitor changes in audio focus and be prepared to resume normal playback when you regain the focus. When the blocking app abandons focus you'll receive a a callback (AUDIOFOCUS_GAIN) . At this point you can restore the volume to normal level or restart playback.

Permanent loss of focus
If the audio focus loss is permanent (AUDIOFOCUS_LOSS), another application is playing audio. Your app should pause play immediately. At this point your app will never receive an AUDIOFOCUS_GAIN callback. To restart playback the user must take an explicit action, like pressing the play transport control in a notification or app UI.

The following code snippet demonstrates how to implement the OnAudioFocusChangeListener and its onAudioFocusChange() callback. Notice the use of a Handler to delay the stop callback on a permanent loss of audio focus.

private Handler mHandler = new Handler();
AudioManager.OnAudioFocusChangeListener afChangeListener =
  new AudioManager.OnAudioFocusChangeListener() {
    public void onAudioFocusChange(int focusChange) {
      if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
        // Permanent loss of audio focus
        // Pause playback immediately
        mediaController.getTransportControls().pause();
        // Wait 30 seconds before stopping playback
        mHandler.postDelayed(mDelayedStopRunnable,
          TimeUnit.SECONDS.toMillis(30));
      }
      else if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT) {
        // Pause playback
      } else if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
        // Lower the volume, keep playing
      } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
        // Your app has been granted audio focus again
        // Raise volume to normal, restart playback if necessary
      }
    }
  };

The handler uses a Runnable that looks like this:

private Runnable mDelayedStopRunnable = new Runnable() {
    @Override
    public void run() {
        mediaController.getTransportControls().stop();
    }
};

To ensure the delayed stop does not kick in if the user restarts playback, call mHandler.removeCallbacks(mDelayedStopRunnable) in response to any state changes. For example, call removeCallbacks() in your Callback's onPlay(), onSkipToNext(), etc. You should also call this method in your service's onDestroy() callback when cleaning up the resources used by your service.

This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a one-minute survey?
Help us improve Android tools and documentation.