lightbulb_outline Help shape the future of the Google Play Console, Android Studio, and Firebase. Start survey

Handling changes in audio output

Users expect to be able to control the volume of an audio app. Standard behavior includes the ability to use the volume controls (either buttons or knobs on the device or sliders in the UI), and to avoid suddenly playing out loud if a peripheral like headphones is disconnected while in use.

Using 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 an alarm clock, you should 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 in your app’s lifecycle, typically from the onResume() 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.

Controlling stream volume programmatically

In rare cases, you can set the volume of an audio stream programmatically. For example, when your app replaces an existing UI. This is not recommended because the Android AudioManager mixes all audio streams of the same type together. These methods change the volume of every app that uses the stream. Avoid using them:

Working with fixed-volume devices

Some devices (like Chromebooks) have volume controls, but they do not allow apps to use the AudioManager methods described above to change the level of an audio stream. These are called fixed-volume devices. You can discover if your app is running on a fixed-volume device by calling isVolumeFixed().

It's a best practice to provide in-app volume controls when your app is running on a fixed-volume device. This provides the ability to balance the volume of your app with other apps that might be playing on the same stream.

You can control your app's audio contribution to the output stream by setting the volume of its player or track. The appropriate methods are shown in the following table:

Player Method
AudioTrack AudioTrack.setVolume()
MediaPlayer MediaPlayer.setVolume()
ExoPlayer Use SimpleExoPlayer.setVolume() which sets the volume of the underlying AudioTrack.

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);
  }
}