Mediensitzungen bieten eine universelle Möglichkeit, mit einem Audio- oder Videoplayer zu interagieren. In Media3 ist der Standardplayer die Klasse ExoPlayer
, die die Player
-Schnittstelle implementiert. Durch das Verbinden der Mediensitzung mit dem Player kann eine App die Medienwiedergabe extern bewerben und Wiedergabebefehle von externen Quellen empfangen.
Befehle können über physische Tasten wie die Wiedergabetaste auf einem Headset oder die Fernbedienung des Fernsehers ausgeführt werden. Sie können auch von Client-Apps mit einem Mediencontroller stammen, z. B. mit Anweisungen zum Pausieren an Google Assistant. In der Mediensitzung werden diese Befehle an den Player der Medien-App delegiert.
Wann sollte eine Mediensitzung ausgewählt werden?
Wenn du MediaSession
implementierst, erlaubst du Nutzern, die Wiedergabe zu steuern:
- Über ihre Kopfhörer Häufig gibt es Schaltflächen oder Touch-Interaktionen, die Nutzer über ihre Kopfhörer ausführen können, um Medien wiederzugeben oder zu pausieren oder zum nächsten oder vorherigen Titel zu wechseln.
- Durch Sprechen mit Google Assistant Ein gängiges Muster ist "Ok Google, Pause", um Medien anzuhalten, die gerade auf dem Gerät abgespielt werden.
- Über die Wear OS-Smartwatch. So können Sie einfacher auf die gängigsten Wiedergabesteuerungen zugreifen, während Sie auf dem Smartphone spielen.
- Über die Mediensteuerung Dieses Karussell zeigt Steuerelemente für jede laufende Mediensitzung.
- Auf Fernsehern. Ermöglicht Aktionen mit physischen Wiedergabeschaltflächen, der Wiedergabesteuerung über die Plattform und der Energieverwaltung. Wenn beispielsweise der Fernseher, die Soundbar oder der AV-Receiver ausgeschaltet oder der Eingang gewechselt wird, sollte die Wiedergabe in der App beendet werden.
- Und alle anderen externen Prozesse, die die Wiedergabe beeinflussen müssen.
Dies eignet sich für viele Anwendungsfälle. Insbesondere in folgenden Fällen solltest du MediaSession
verwenden:
- Sie streamen Videos im Langformat, z. B. Filme oder Live-TV.
- Sie streamen längere Audioinhalte wie Podcasts oder Musikplaylists.
- Sie erstellen eine TV-App.
Allerdings eignen sich nicht alle Anwendungsfälle für MediaSession
. In den folgenden Fällen kann es sinnvoll sein, nur Player
zu verwenden:
- Du präsentierst Kurzinhalte, bei denen die Nutzerinteraktion und -interaktion entscheidend sind.
- Es gibt nicht nur ein aktives Video, z. B. wenn der Nutzer durch eine Liste scrollt und mehrere Videos gleichzeitig auf dem Bildschirm angezeigt werden.
- Du spielst ein einmaliges Einführungs- oder Erklärungsvideo ab, das sich dein Nutzer wahrscheinlich aktiv ansieht.
- Bei Ihren Inhalten muss Datenschutz beachtet werden und Sie möchten nicht, dass externe Prozesse auf die Medienmetadaten zugreifen (z. B. der Inkognitomodus in einem Browser).
Wenn dein Anwendungsfall zu keinem der oben genannten Punkte passt, solltest du überlegen, ob du damit einverstanden bist, dass deine App die Wiedergabe fortsetzt, wenn der Nutzer nicht aktiv mit den Inhalten interagiert. Wenn die Antwort „Ja“ lautet, sollten Sie MediaSession
auswählen. Wenn die Antwort „Nein“ lautet, sollten Sie stattdessen Player
verwenden.
Mediensitzung erstellen
Neben dem von ihm verwalteten Player befindet sich eine Mediensitzung. Sie können eine Mediensitzung mit einem Context
- und einem Player
-Objekt erstellen. Sie sollten eine Mediensitzung bei Bedarf erstellen und initialisieren, z. B. die Lebenszyklusmethode onStart()
oder onResume()
der Activity
oder Fragment
oder die onCreate()
-Methode der Service
, der die Mediensitzung und der zugehörige Player gehören.
Initialisieren Sie zum Erstellen einer Mediensitzung ein Player
und geben Sie es so an MediaSession.Builder
an:
Kotlin
val player = ExoPlayer.Builder(context).build() val mediaSession = MediaSession.Builder(context, player).build()
Java
ExoPlayer player = new ExoPlayer.Builder(context).build(); MediaSession mediaSession = new MediaSession.Builder(context, player).build();
Automatische Statusverarbeitung
Die Media3-Bibliothek aktualisiert die Mediensitzung automatisch anhand des Status des Players. Daher müssen Sie die Zuordnung von Spieler zu Sitzung nicht manuell vornehmen.
Dies ist eine Abweichung vom Legacy-Ansatz, bei dem ein PlaybackStateCompat
unabhängig vom Player selbst erstellt und verwaltet werden muss, um beispielsweise Fehler anzuzeigen.
Eindeutige Sitzungs-ID
Standardmäßig erstellt MediaSession.Builder
eine Sitzung mit einem leeren String als Sitzungs-ID. Das ist ausreichend, wenn eine Anwendung nur eine einzige Sitzungsinstanz erstellen soll, was in der Regel der Fall ist.
Wenn eine Anwendung mehrere Sitzungsinstanzen gleichzeitig verwalten möchte, muss sie dafür sorgen, dass die Sitzungs-ID jeder Sitzung eindeutig ist. Die Sitzungs-ID kann beim Erstellen der Sitzung mit MediaSession.Builder.setId(String id)
festgelegt werden.
Wenn ein IllegalStateException
Ihre Anwendung abstürzt und die Fehlermeldung IllegalStateException: Session ID must be unique. ID=
anzeigt, wurde wahrscheinlich unerwartet eine Sitzung erstellt, bevor eine zuvor erstellte Instanz mit derselben ID freigegeben wurde. Um zu verhindern, dass Sitzungen durch einen Programmierfehler gehackt werden, werden solche Fälle erkannt und durch das Auslösen einer Ausnahme benachrichtigt.
Anderen Kunden Kontrolle gewähren
Die Mediensitzung ist der Schlüssel zur Steuerung der Wiedergabe. Damit können Sie Befehle von externen Quellen an den Player weiterleiten, der die Wiedergabe Ihrer Medien übernimmt. Bei diesen Quellen kann es sich um physische Tasten wie die Wiedergabetaste auf einem Headset oder die Fernbedienung des Fernsehers oder indirekte Befehle handeln, z. B. um Anweisungen zum Pausieren von Google Assistant. Ebenso kannst du Zugriff auf das Android-System gewähren, um die Steuerung von Benachrichtigungen und Sperrbildschirmen zu erleichtern, oder auf eine Wear OS-Smartwatch, damit du die Wiedergabe über das Zifferblatt steuern kannst. Externe Clients können einen Mediencontroller verwenden, um Wiedergabebefehle an Ihre Medien-App auszugeben. Diese werden von Ihrer Mediensitzung empfangen, die schließlich Befehle an den Mediaplayer delegiert.
Wenn ein Controller eine Verbindung zu Ihrer Mediensitzung herstellen möchte, wird die Methode onConnect()
aufgerufen. Anhand des bereitgestellten ControllerInfo
kannst du entscheiden, ob du die Anfrage akzeptieren oder ablehnen möchtest. Ein Beispiel für die Annahme einer Verbindungsanfrage finden Sie im Abschnitt Verfügbare Befehle deklarieren.
Nach dem Verbinden kann ein Controller Wiedergabebefehle an die Sitzung senden. Die Sitzung delegiert diese Befehle dann an den Player. Wiedergabe- und Wiedergabelistenbefehle, die in der Player
-Oberfläche definiert sind, werden von der Sitzung automatisch verarbeitet.
Mit anderen Callback-Methoden kannst du beispielsweise Anfragen für benutzerdefinierte Wiedergabebefehle oder die Änderung der Playlist verarbeiten.
Diese Callbacks enthalten in ähnlicher Weise ein ControllerInfo
-Objekt, sodass du für jeden Controller modifizieren kannst, wie du auf die einzelnen Anfragen antwortest.
Playlist ändern
Eine Mediasitzung kann die Playlist ihres Players direkt ändern, wie im ExoPlayer-Leitfaden für Playlists erläutert.
Controller können die Playlist auch ändern, wenn entweder COMMAND_SET_MEDIA_ITEM
oder COMMAND_CHANGE_MEDIA_ITEMS
für den Controller verfügbar ist.
Wenn der Playlist neue Elemente hinzugefügt werden, benötigt der Player normalerweise MediaItem
-Instanzen mit einem definierten URI, damit sie abgespielt werden können. Standardmäßig werden neu hinzugefügte Elemente automatisch an Spielermethoden wie player.addMediaItem
weitergeleitet, wenn für sie ein URI definiert ist.
Wenn du die dem Player hinzugefügten MediaItem
-Instanzen anpassen möchtest, kannst du onAddMediaItems()
überschreiben.
Dieser Schritt ist erforderlich, wenn Sie Controller unterstützen möchten, die Medien ohne definierten URI anfordern. Stattdessen ist für MediaItem
normalerweise eines oder mehrere der folgenden Felder festgelegt, um das angeforderte Medium zu beschreiben:
MediaItem.id
: Eine generische ID zur Identifizierung des Mediums.MediaItem.RequestMetadata.mediaUri
: Ein Anfrage-URI, der ein benutzerdefiniertes Schema verwenden kann und vom Spieler nicht unbedingt direkt wiedergegeben werden kann.MediaItem.RequestMetadata.searchQuery
: Eine Suchanfrage in Textform, zum Beispiel von Google Assistant.MediaItem.MediaMetadata
: Strukturierte Metadaten wie „Titel“ oder „Interpret“.
Für weitere Anpassungsoptionen für völlig neue Playlists kannst du zusätzlich onSetMediaItems()
überschreiben, mit dem du den Startelement und die Position in der Playlist definieren kannst. Sie können beispielsweise ein einzelnes angefordertes Element auf eine ganze Playlist erweitern und den Spieler anweisen, beim Index des ursprünglich angeforderten Elements zu beginnen. Eine Beispielimplementierung von onSetMediaItems()
mit dieser Funktion finden Sie in der Sitzungsdemo-App.
Benutzerdefiniertes Layout und benutzerdefinierte Befehle verwalten
In den folgenden Abschnitten wird beschrieben, wie Sie Clientanwendungen ein benutzerdefiniertes Layout benutzerdefinierter Befehlsschaltflächen anbieten und Controller autorisieren, die benutzerdefinierten Befehle zu senden.
Benutzerdefiniertes Layout der Sitzung definieren
Um Client-Apps anzugeben, welche Wiedergabesteuerungen für den Nutzer eingeblendet werden sollen, legen Sie das benutzerdefinierte Layout der Sitzung fest, wenn Sie MediaSession
in der Methode onCreate()
Ihres Dienstes erstellen.
Kotlin
override fun onCreate() { super.onCreate() val likeButton = CommandButton.Builder() .setDisplayName("Like") .setIconResId(R.drawable.like_icon) .setSessionCommand(SessionCommand(SessionCommand.COMMAND_CODE_SESSION_SET_RATING)) .build() val favoriteButton = CommandButton.Builder() .setDisplayName("Save to favorites") .setIconResId(R.drawable.favorite_icon) .setSessionCommand(SessionCommand(SAVE_TO_FAVORITES, Bundle())) .build() session = MediaSession.Builder(this, player) .setCallback(CustomMediaSessionCallback()) .setCustomLayout(ImmutableList.of(likeButton, favoriteButton)) .build() }
Java
@Override public void onCreate() { super.onCreate(); CommandButton likeButton = new CommandButton.Builder() .setDisplayName("Like") .setIconResId(R.drawable.like_icon) .setSessionCommand(new SessionCommand(SessionCommand.COMMAND_CODE_SESSION_SET_RATING)) .build(); CommandButton favoriteButton = new CommandButton.Builder() .setDisplayName("Save to favorites") .setIconResId(R.drawable.favorite_icon) .setSessionCommand(new SessionCommand(SAVE_TO_FAVORITES, new Bundle())) .build(); Player player = new ExoPlayer.Builder(this).build(); mediaSession = new MediaSession.Builder(this, player) .setCallback(new CustomMediaSessionCallback()) .setCustomLayout(ImmutableList.of(likeButton, favoriteButton)) .build(); }
Verfügbare Player- und benutzerdefinierte Befehle deklarieren
Medienanwendungen können benutzerdefinierte Befehle definieren, die beispielsweise in einem benutzerdefinierten Layout verwendet werden können. Beispielsweise können Sie Schaltflächen implementieren, mit denen Nutzer ein Medienelement in einer Liste von Lieblingselementen speichern können. MediaController
sendet benutzerdefinierte Befehle und MediaSession.Callback
empfängt sie.
Sie können festlegen, welche benutzerdefinierten Sitzungsbefehle für ein MediaController
verfügbar sind, wenn es eine Verbindung zu Ihrer Mediensitzung herstellt. Dazu überschreiben Sie MediaSession.Callback.onConnect()
. Konfigurieren Sie die verfügbaren Befehle und geben Sie sie zurück, wenn Sie eine Verbindungsanfrage von einer MediaController
in der Callback-Methode onConnect
akzeptieren:
Kotlin
private inner class CustomMediaSessionCallback: MediaSession.Callback { // Configure commands available to the controller in onConnect() override fun onConnect( session: MediaSession, controller: MediaSession.ControllerInfo ): MediaSession.ConnectionResult { val sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() .add(SessionCommand(SAVE_TO_FAVORITES, Bundle.EMPTY)) .build() return AcceptedResultBuilder(session) .setAvailableSessionCommands(sessionCommands) .build() } }
Java
class CustomMediaSessionCallback implements MediaSession.Callback { // Configure commands available to the controller in onConnect() @Override public ConnectionResult onConnect( MediaSession session, ControllerInfo controller) { SessionCommands sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() .add(new SessionCommand(SAVE_TO_FAVORITES, new Bundle())) .build(); return new AcceptedResultBuilder(session) .setAvailableSessionCommands(sessionCommands) .build(); } }
Wenn Sie benutzerdefinierte Befehlsanfragen von einem MediaController
erhalten möchten, überschreiben Sie die Methode onCustomCommand()
in Callback
.
Kotlin
private inner class CustomMediaSessionCallback: MediaSession.Callback { ... override fun onCustomCommand( session: MediaSession, controller: MediaSession.ControllerInfo, customCommand: SessionCommand, args: Bundle ): ListenableFuture<SessionResult> { if (customCommand.customAction == SAVE_TO_FAVORITES) { // Do custom logic here saveToFavorites(session.player.currentMediaItem) return Futures.immediateFuture( SessionResult(SessionResult.RESULT_SUCCESS) ) } ... } }
Java
class CustomMediaSessionCallback implements MediaSession.Callback { ... @Override public ListenableFuture<SessionResult> onCustomCommand( MediaSession session, ControllerInfo controller, SessionCommand customCommand, Bundle args ) { if(customCommand.customAction.equals(SAVE_TO_FAVORITES)) { // Do custom logic here saveToFavorites(session.getPlayer().getCurrentMediaItem()); return Futures.immediateFuture( new SessionResult(SessionResult.RESULT_SUCCESS) ); } ... } }
Mithilfe der packageName
-Eigenschaft des MediaSession.ControllerInfo
-Objekts, das an Callback
-Methoden übergeben wird, können Sie verfolgen, welcher Mediacontroller eine Anfrage stellt. Auf diese Weise können Sie das Verhalten Ihrer Anwendung als Reaktion auf einen bestimmten Befehl anpassen, wenn dieser aus dem System, Ihrer eigenen Anwendung oder anderen Clientanwendungen stammt.
Benutzerdefiniertes Layout nach einer Nutzerinteraktion aktualisieren
Nachdem du einen benutzerdefinierten Befehl oder eine andere Interaktion mit deinem Player ausgeführt hast, kannst du das auf der Controller-Benutzeroberfläche angezeigte Layout aktualisieren. Ein typisches Beispiel ist eine Ein/Aus-Schaltfläche, deren Symbol sich ändert, nachdem die mit dieser Schaltfläche verbundene Aktion ausgelöst wurde. Mit MediaSession.setCustomLayout
können Sie das Layout aktualisieren:
Kotlin
val removeFromFavoritesButton = CommandButton.Builder() .setDisplayName("Remove from favorites") .setIconResId(R.drawable.favorite_remove_icon) .setSessionCommand(SessionCommand(REMOVE_FROM_FAVORITES, Bundle())) .build() mediaSession.setCustomLayout(ImmutableList.of(likeButton, removeFromFavoritesButton))
Java
CommandButton removeFromFavoritesButton = new CommandButton.Builder() .setDisplayName("Remove from favorites") .setIconResId(R.drawable.favorite_remove_icon) .setSessionCommand(new SessionCommand(REMOVE_FROM_FAVORITES, new Bundle())) .build(); mediaSession.setCustomLayout(ImmutableList.of(likeButton, removeFromFavoritesButton));
Verhalten von Wiedergabebefehlen anpassen
Wenn Sie das Verhalten eines in der Player
-Schnittstelle definierten Befehls anpassen möchten, z. B. play()
oder seekToNext()
, müssen Sie Player
mit einem ForwardingPlayer
zusammenfassen.
Kotlin
val player = ExoPlayer.Builder(context).build() val forwardingPlayer = object : ForwardingPlayer(player) { override fun play() { // Add custom logic super.play() } override fun setPlayWhenReady(playWhenReady: Boolean) { // Add custom logic super.setPlayWhenReady(playWhenReady) } } val mediaSession = MediaSession.Builder(context, forwardingPlayer).build()
Java
ExoPlayer player = new ExoPlayer.Builder(context).build(); ForwardingPlayer forwardingPlayer = new ForwardingPlayer(player) { @Override public void play() { // Add custom logic super.play(); } @Override public void setPlayWhenReady(boolean playWhenReady) { // Add custom logic super.setPlayWhenReady(playWhenReady); } }; MediaSession mediaSession = new MediaSession.Builder(context, forwardingPlayer).build();
Weitere Informationen zu ForwardingPlayer
findest du im ExoPlayer-Leitfaden unter Anpassung.
Anfordernden Controller eines Spielerbefehls identifizieren
Wenn ein Aufruf einer Player
-Methode von einem MediaController
ausgeht, können Sie die Herkunftsquelle mit MediaSession.controllerForCurrentRequest
identifizieren und die ControllerInfo
für die aktuelle Anfrage abrufen:
Kotlin
class CallerAwareForwardingPlayer(player: Player) : ForwardingPlayer(player) { override fun seekToNext() { Log.d( "caller", "seekToNext called from package ${session.controllerForCurrentRequest?.packageName}" ) super.seekToNext() } }
Java
public class CallerAwareForwardingPlayer extends ForwardingPlayer { public CallerAwareForwardingPlayer(Player player) { super(player); } @Override public void seekToNext() { Log.d( "caller", "seekToNext called from package: " + session.getControllerForCurrentRequest().getPackageName()); super.seekToNext(); } }
Auf Medienschaltflächen reagieren
Medientasten sind Hardwaretasten auf Android-Geräten und anderen Peripheriegeräten, z. B. die Wiedergabe-/Pause-Taste an einem Bluetooth-Headset. Media3 verarbeitet Medienschaltflächenereignisse für Sie, wenn sie in der Sitzung ankommen, und ruft die entsprechende Player
-Methode im Sitzungsplayer auf.
Eine App kann das Standardverhalten durch Überschreiben von MediaSession.Callback.onMediaButtonEvent(Intent)
überschreiben. In diesem Fall kann/muss die Anwendung alle API-Spezifikationen selbstständig verarbeiten.