Background playback with a MediaSessionService

It is often desirable to play media while an app is not in the foreground. For example, a music player generally keeps playing music when the user has locked their device or is using another app. The Media3 library provides a series of interfaces that allow you to support background playback.

Use a MediaSessionService

To enable background playback, you should contain the Player and MediaSession inside a separate Service. This allows the device to continue serving media even while your app is not in the foreground.

The MediaSessionService allows the media session to run separately
  from the app's activity
Figure 1: The MediaSessionService allows the media session to run separately from the app's activity

When hosting a player inside a Service, you should use a MediaSessionService. To do this, create a class that extends MediaSessionService` and create your media session inside of it.

Using MediaSessionService makes it possible for external clients like Google Assistant, system media controls, or companion devices like Wear OS to discover your service, connect to it, and control playback, all without accessing your app's UI activity at all. In fact, there can be multiple client apps connected to the same MediaSessionService at the same time, each app with its own MediaController.

Provide access to the media session

Override the onGetSession() method to give other clients access to your media session.

Kotlin

class PlaybackService : MediaSessionService() {
  private var mediaSession: MediaSession? = null

  // If desired, validate the controller before returning the media session
  override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? =
    mediaSession

  // Create your player and media session in the onCreate lifecycle event
  override fun onCreate() {
    super.onCreate()
    val player = ExoPlayer.Builder(this).build()
    mediaSession = MediaSession.Builder(this, player).build()
  }

  // Remember to release the player and media session in onDestroy
  override fun onDestroy() {
    mediaSession?.run {
      player.release()
      release()
      mediaSession = null
    }
    super.onDestroy()
  }
}

Java

public class PlaybackService extends MediaSessionService {
  private MediaSession mediaSession = null;

  @Override
  public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) {
    // If desired, validate the controller before returning the media session
    return mediaSession;
  }

  // Create your Player and MediaSession in the onCreate lifecycle event
  @Override
  public void onCreate() {
    super.onCreate();
    ExoPlayer player = new ExoPlayer.Builder(this).build();
    mediaSession = new MediaSession.Builder(this, player).build();
  }

  // Remember to release the player and media session in onDestroy
  @Override
  public void onDestroy() {
    mediaSession.getPlayer().release();
    mediaSession.release();
    mediaSession = null;
    super.onDestroy();
  }
}

An app requires permission to run a foreground service. Add the FOREGROUND_SERVICE permission to the manifest, and if you target API 34 and above also FOREGROUND_SERVICE_MEDIA_PLAYBACK:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />

You must also declare your Service class in the manifest with an intent filter of MediaSessionService.

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaSessionService"/>
    </intent-filter>
</service>

You must define a foregroundServiceType that includes mediaPlayback when your app is running on a device with Android 10 (API level 29) and higher.

Control playback in the media session

In the Activity or Fragment containing your player UI, you can establish a link between the UI and your media session using a MediaController. Your UI uses the media controller to send commands from your UI to the player within the session. See the Create a MediaController guide for details on creating and using a MediaController.

Handle UI commands

The MediaSession receives commands from the controller through its MediaSession.Callback. Initializing a MediaSession creates a default implementation of MediaSession.Callback that automatically handles all commands a MediaController sends to your player.

Notification

A MediaSessionService automatically creates a MediaNotification for you that should work in most cases. By default, the published notification is a MediaStyle notification that stays updated with the latest information from your media session and displays playback controls. The MediaNotification is aware of your session and can be used to control playback for any other apps that are connected to the same session.

For example, a music streaming app using a MediaSessionService would create a MediaNotification that displays the title, artist, and album art for the current song playing alongside playback controls based on your MediaSession configuration.

To provide a custom notification, create a MediaNotification.Provider with DefaultMediaNotificationProvider.Builder or by creating a custom implementation of the provider interface. Add your provider to your MediaSession with setMediaNotificationProvider.