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:
- Android system media controls
- Android Wear OS companion app
- Android Auto and Automotive OS
- Voice assistants, like Google Assistant
- The Media Controller Test app
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());