Managing Multiple Sound Sources in Android with Audio Focus

Share this article

Sound is a great way to grab user attention, give interface feedback or immerse a player in a game. Imagine if multiple apps all tried to play sounds at the same time. This would result in an unintelligible cacophony and make users reach for the mute button. Android provides a simple API to play music and audio effects and manage different sources. The Android audio focus API lets an app request ‘audio focus’ and lets the app know if it has lost focus so it can react. In this tutorial I will show how to use these APIs in your own apps.

You can find the final code for this tutorial on GitHub.

Requesting Audio Focus for Your App

You should request audio focus in your app before playing any sound. To do this you need to get an instance of the AudioManager. Once you have the instance you can then use requestAudioFocus.

Create a new app with an empty activity and replace the contents of activity_main.xml with the following:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView android:text="Audio Focus"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <Button
            android:id="@+id/btnRequestFocus"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Request Audio Focus"/>

        <Button
            android:id="@+id/btnReleaseFocus"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Release Audio Focus"/>

    </LinearLayout>

</RelativeLayout>

This layout uses the LinearLayout style that contains two buttons. One to request focus and one to release it. Update the contents MainActivity.java to the following:

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button btnRequestFocus = (Button)findViewById(R.id.btnRequestFocus);
        btnRequestFocus.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                boolean gotFocus = requestAudioFocusForMyApp(MainActivity.this);
                if(gotFocus) {
                    //play audio.
                }
            }
        });


        Button btnReleaseFocus = (Button)findViewById(R.id.btnReleaseFocus);
        btnReleaseFocus.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //Stop playing audio.
                releaseAudioFocusForMyApp(MainActivity.this);
            }
        });
    }

    private boolean requestAudioFocusForMyApp(final Context context) {
        AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);

        // Request audio focus for playback
        int result = am.requestAudioFocus(null,
                // Use the music stream.
                AudioManager.STREAM_MUSIC,
                // Request permanent focus.
                AudioManager.AUDIOFOCUS_GAIN);

        if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
            Log.d("AudioFocus", "Audio focus received");
            return true;
        } else {
            Log.d("AudioFocus", "Audio focus NOT received");
            return false;
        }
    }

    void releaseAudioFocusForMyApp(final Context context) {
        AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
        am.abandonAudioFocus(null);
    }
}

This activity adds click listeners to both buttons. When a user clicks on the Request Audio Focus button it gets the AudioManager instance by calling the getSystemService API and passes the Context.AUDIO_SERVICE context. Once you have the AudioManager, you can request focus by calling the requestAudioFocus API which accepts AudioManager.OnAudioFocusChangeListener as its first parameter. You will see this in more detail later, but for now pass null. The second parameter is the stream type which could be one of the following, depending on the type of sound you want to use:

  • STREAM_ALARM: The audio is an alarm.
  • STREAM_MUSIC: The audio is media like music, video, background sound etc.
  • STREAM_NOTIFICATION: The audio is for a notification.
  • STREAM_RING: The audio is phone ringtone.
  • STREAM_SYSTEM: The audio is a system sound.
  • STREAM_DTMF: The audio is a DTFM tone.

The third parameter is the type of focus you want, for example a permanent or transient focus. It accepts one of the following parameters:

  • AUDIOFOCUS_GAIN: When you want focus for a long time to play audio of a long duration.
  • AUDIOFOCUS_GAIN_TRANSIENT: When you want focus for a short time.
  • AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK: When you want focus for a short time and it’s OK for other apps to ‘duck’ rather than stop the audio.

Once you call requestAudioFocus with the appropriate parameters, the API will either return AUDIOFOCUS_REQUEST_GRANTED or AUDIOFOCUS_REQUEST_FAILED. The audio focus request can fail if higher priority sound like a phone call is in progress and you should only start playing audio if the requestAudioFocus API succeeds.

The activity above requested audio focus with the stream type as STREAM_MUSIC and duration as AUDIOFOCUS_GAIN to play a media file for a long time. Once the audio completes, release the focus using the AudioManager abandonAudioFocus API. In the example app, this happens when a user clicks the Release Audio Focus button.

App Example

Handling Audio Focus Events in an App

When your app receives focus it can pass an AudioManager.OnAudioFocusChangeListener which provides callbacks for when an focus change happens.

Suppose your app gains audio focus, and another app requests transient audio focus, focus will be given to the other app. Android will notify your app via an OnAudioFocusChangeListener so that your app can respond to the change. Once the other app abandons its focus your app will regain focus and receive an appropriate callback. This is conceptually similar to the Activity life-cycle events like onStart, OnStop, OnPause, OnResume etc.

To receive focus events, you need to pass an instance of AudioManager.OnAudioFocusChangeListener to the requestAudioFocus and abandonAudioFocus calls. Update code MainActivity.java to the following to receive the callbacks:

public class MainActivity extends Activity {

    private final static String TAG = "AudioFocus";
    private AudioManager.OnAudioFocusChangeListener mOnAudioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {

        @Override
        public void onAudioFocusChange(int focusChange) {
            switch (focusChange) {
                case AudioManager.AUDIOFOCUS_GAIN:
                    Log.i(TAG, "AUDIOFOCUS_GAIN");
                    //restart/resume your sound
                    break;
                case AudioManager.AUDIOFOCUS_LOSS:
                    Log.e(TAG, "AUDIOFOCUS_LOSS");
                    //Loss of audio focus for a long time
                    //Stop playing the sound
                    break;
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                    Log.e(TAG, "AUDIOFOCUS_LOSS_TRANSIENT");
                    //Loss of audio focus for a short time
                    //Pause playing the sound
                    break;
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                    Log.e(TAG, "AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK");
                    //Loss of audio focus for a short time.
                    //But one can duck. Lower the volume of playing the sound
                    break;

                default:
                    //
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button btnRequestFocus = (Button)findViewById(R.id.btnRequestFocus);
        btnRequestFocus.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                boolean gotFocus = requestAudioFocusForMyApp(MainActivity.this);
                if(gotFocus) {
                    //play audio.
                }
            }
        });


        Button btnReleaseFocus = (Button)findViewById(R.id.btnReleaseFocus);
        btnReleaseFocus.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //Stop playing audio.
                releaseAudioFocusForMyApp(MainActivity.this);
            }
        });
    }

    private boolean requestAudioFocusForMyApp(final Context context) {
        AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);

        // Request audio focus for playback
        int result = am.requestAudioFocus(mOnAudioFocusChangeListener,
                // Use the music stream.
                AudioManager.STREAM_MUSIC,
                // Request permanent focus.
                AudioManager.AUDIOFOCUS_GAIN);

        if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
            Log.d(TAG, "Audio focus received");
            return true;
        } else {
            Log.d(TAG, "Audio focus NOT received");
            return false;
        }
    }

    void releaseAudioFocusForMyApp(final Context context) {
        AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
        am.abandonAudioFocus(mOnAudioFocusChangeListener);
    }
}

In the AudioManager.OnAudioFocusChangeListener you need to override the onAudioFocusChange function to which the focus change event is passed.

The events can be one of the following:

  • AUDIOFOCUS_LOSS: Audio focus is lost by the app. You should stop playing the sound and release any assets acquired to play the sound.
  • AUDIOFOCUS_LOSS_TRANSIENT: Audio focus is lost by the app for a short period of time. You should pause the audio.
  • AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: Audio focus is lost by the app for a short period of time. You can continue to play the audio but should lower the volume.
  • AUDIOFOCUS_GAIN: Your app has regained audio focus after a loss. You should restart it and increase the volume if decreased in a prior event.

Ducking

At times the Android system or other apps may need to play short high priority sounds. In these cases it will request audio focus as AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK. If your app held audio focus you will receive the event as AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK and you should continue to play sound, but lower the volume. If you are using the standard android MediaPlayer you can lower the volume using the setVolume function.

Respecting Audio Focus

Imagine a conference where all the speakers spoke into the mike at the same time. It would be total chaos and audience members will leave.

Whilst your app isn’t required to respect the focus of the Android system or other apps, it’s a good user experience to so. If you don’t your app will receive bad reviews or frequent uninstalls.

What you app does to react to these events depends on the intended functionality, but it’s important to take this into account.

If you have any comments or questions, please let me know below.

Frequently Asked Questions (FAQs) on Managing Multiple Sound Sources in Android with Audio Focus

How Can I Manage Audio Focus Across Multiple Apps on Android?

Managing audio focus across multiple apps on Android involves understanding how audio focus works. When an app plays audio, it requests audio focus. If another app is already playing audio, it will lose focus and stop playing. To manage audio focus across multiple apps, you need to implement audio focus change listeners in your apps. These listeners will respond to changes in audio focus, allowing your apps to pause, resume, or lower their volume as necessary.

What is the Role of AudioManager in Android Audio Focus?

AudioManager is a system service in Android that handles audio-related tasks. It plays a crucial role in managing audio focus. You can use AudioManager to request audio focus, abandon it, or register an audio focus change listener. It provides methods like requestAudioFocus() and abandonAudioFocus() to manage audio focus.

Can Two Apps Play Audio Simultaneously on Android?

By default, when one app requests audio focus, any other app currently holding audio focus will lose it. However, it’s possible for two apps to play audio simultaneously by using the AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK audio focus request. This request allows an app to continue playing audio at a lower volume when another app requests audio focus.

How Can I Implement Audio Focus Change Listeners in My App?

Implementing audio focus change listeners involves creating an AudioManager.OnAudioFocusChangeListener and overriding its onAudioFocusChange() method. This method will be called whenever there’s a change in audio focus, allowing your app to respond appropriately.

What Happens When My App Loses Audio Focus?

When your app loses audio focus, it should either pause its audio or lower its volume, depending on the type of audio focus loss. For instance, if it’s a transient loss, your app should pause its audio and resume when it regains focus. If it’s a transient loss that allows ducking, your app should lower its volume and restore it when it regains focus.

How Can I Request Audio Focus in My App?

Requesting audio focus involves calling the requestAudioFocus() method of AudioManager. You need to specify the type of audio focus request and the stream type. You also need to provide an AudioManager.OnAudioFocusChangeListener that will be notified of any changes in audio focus.

What Are the Different Types of Audio Focus in Android?

There are four types of audio focus in Android: AUDIOFOCUS_GAIN, AUDIOFOCUS_GAIN_TRANSIENT, AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, and AUDIOFOCUS_LOSS. Each type represents a different way your app can interact with other audio apps.

Can I Control the Volume of My App When It Loses Audio Focus?

Yes, you can control the volume of your app when it loses audio focus. If your app loses audio focus in a way that allows ducking, it can continue playing audio at a lower volume. You can control this volume using the setStreamVolume() method of AudioManager.

How Can I Abandon Audio Focus When My App is Done Playing Audio?

Abandoning audio focus when your app is done playing audio involves calling the abandonAudioFocus() method of AudioManager. This tells the system that your app no longer needs audio focus, allowing other apps to gain focus.

What Are the Best Practices for Managing Audio Focus in Android?

Some best practices for managing audio focus in Android include always requesting audio focus when your app needs to play audio, handling audio focus loss gracefully, and abandoning audio focus when your app is done playing audio. It’s also important to test your app in various audio scenarios to ensure it behaves correctly.

Abbas SuterwalaAbbas Suterwala
View Author

Abbas is a software engineer by profession and a passionate coder who lives every moment to the fullest. He loves open source projects and WordPress. When not chilling around with friends he's occupied with one of the following open source projects he's built: Choomantar, The Browser Counter WordPress plugin, and Google Buzz From Admin.

apiaudiochriswgooglemusicsound
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week