Connect to a media app

A media controller interacts with a media session to query and control a media app's playback. In Media3, the MediaController API implements the Player interface. Examples of client apps that use a media controller include:

A media controller can also be useful within a media app, for example if the player and media session live in a Service separate from the Activity or Fragment with the UI.

Create a MediaController

To create a MediaController, start by creating a SessionToken for the corresponding MediaSession. The onStart() method of your Activity or Fragment can be a good place for this.

Kotlin

val sessionToken = 
  SessionToken(context, ComponentName(context, PlaybackService::class.java))

Java

SessionToken sessionToken = 
  new SessionToken(context, new ComponentName(context, PlaybackService.class));

Using this SessionToken to then build a MediaController connects the controller to the given session. This takes place asynchronously, so you should listen for the result and use it when available.

Kotlin

val controllerFuture =
  MediaController.Builder(context, sessionToken).buildAsync()
controllerFuture.addListener({
  // MediaController is available here with controllerFuture.get()
}, MoreExecutors.directExecutor())

Java

ListenableFuture<MediaController> controllerFuture =
  new MediaController.Builder(context, sessionToken).buildAsync();
controllerFuture.addListener(() -> {
  // MediaController is available here with controllerFuture.get()
}, MoreExecutors.directExecutor());

Use a MediaController

MediaController implements the Player interface, so you can use the commands defined in the interface to control playback of the connected MediaSession. This is to say that calling play() on a MediaController will send the command to the connected MediaSession, which will subsequently delegate the command to its underlying Player.

Similarly, you can add a Player.Listener to the controller to listen for changes in the Player state. Refer to the Player events guide for more details on using a Player.Listener. The MediaController.Listener interface defines additional callbacks for events and incoming commands from the connected MediaSession, such as onAvailableSessionCommandsChanged() for when the media session changes the available session commands and onDisconnected() for when the controller is disconnected from the session.

As with other components, remember to release the MediaController when it is no longer needed, such as in the onStop() method of an Activity or Fragment.

Kotlin

MediaController.releaseFuture(controllerFuture)

Java

MediaController.releaseFuture(controllerFuture);

Releasing the controller will still deliver all pending commands sent to the session and only unbind from the session service either once these commands have been handled or after a timeout period, whichever occurs first.

Create and use a MediaBrowser

A MediaBrowser builds on top of the capabilities offered by a MediaController to also enable browsing the media library offered by a media app's MediaLibraryService.

Kotlin

val browserFuture = MediaBrowser.Builder(context, sessionToken).buildAsync()
browserFuture.addListener({
  // MediaBrowser is available here with browserFuture.get()
}, MoreExecutors.directExecutor())

Java

ListenableFuture<MediaBrowser> browserFuture =
  new MediaBrowser.Builder(context, sessionToken).buildAsync();
browserFuture.addListener(() -> {
  // MediaBrowser is available here with browserFuture.get()
}, MoreExecutors.directExecutor());

To start browsing the media app's content library, first retrieve the root node with getLibraryRoot():

Kotlin

// Get the library root to start browsing the library tree.
val rootFuture = mediaBrowser.getLibraryRoot(/* params= */ null)
rootFuture.addListener({
  // Root node MediaItem is available here with rootFuture.get().value
}, MoreExecutors.directExecutor())

Java

// Get the library root to start browsing the library tree.
ListenableFuture<LibraryResult<MediaItem>> rootFuture =
  mediaBrowser.getLibraryRoot(/* params= */ null);
rootFuture.addListener(() -> {
  // Root node MediaItem is available here with rootFuture.get().value
}, MoreExecutors.directExecutor());

You can then navigate through the media library by retrieving the children of a MediaItem in the library with getChildren(). For example, to retrieve the children of the root node MediaItem:

Kotlin

// Get the library root to start browsing the library tree.
val childrenFuture = 
  mediaBrowser.getChildren(rootMediaItem.mediaId, 0, Int.MAX_VALUE, null)
childrenFuture.addListener({
  // List of children MediaItem nodes is available here with
  // childrenFuture.get().value
}, MoreExecutors.directExecutor())

Java

ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> childrenFuture =
  mediaBrowser.getChildren(rootMediaItem.mediaId, 0, Integer.MAX_VALUE, null);
childrenFuture.addListener(() -> {
  // List of children MediaItem nodes is available here with
  // childrenFuture.get().value
}, MoreExecutors.directExecutor());