Mediensitzungen bieten eine universelle Möglichkeit, mit einem Audio- oder Videoplayer zu interagieren. In Media3 ist der Standardplayer die Klasse ExoPlayer
, die die Schnittstelle Player
implementiert. Wenn die Mediensitzung mit dem Player verbunden ist, kann eine App die Medienwiedergabe extern ankündigen und Wiedergabebefehle von externen Quellen empfangen.
Befehle können von physischen Tasten wie der Wiedergabetaste auf einem Headset oder der Fernbedienung eines Fernsehers stammen. Sie können auch von Client-Apps mit einem Mediacontroller stammen, z. B. wenn Sie Google Assistant bitten, die Wiedergabe zu pausieren. Die Mediensitzung delegiert diese Befehle an den Player der Medien-App.
Wann sollte ich eine Mediensitzung auswählen?
Wenn du MediaSession
implementierst, können Nutzer die Wiedergabe so steuern:
- Über die Kopfhörer Häufig gibt es Tasten oder Touch-Interaktionen, mit denen Nutzer Medien abspielen oder pausieren oder zum nächsten oder vorherigen Titel springen können.
- Sie können mit Google Assistant sprechen. Ein häufig verwendetes Muster ist „Hey Google, pausiere“, um alle Medien zu pausieren, die gerade auf dem Gerät wiedergegeben werden.
- Über ihre Wear OS-Smartwatch So können Nutzer beim Abspielen auf ihrem Smartphone einfacher auf die gängigsten Wiedergabesteuerungen zugreifen.
- Über die Mediensteuerung In diesem Karussell werden die Steuerelemente für jede laufende Mediensitzung angezeigt.
- Unter TV Ermöglicht Aktionen mit physischen Wiedergabeschaltern, Plattformwiedergabesteuerung und Energieverwaltung. Wenn sich beispielsweise der Fernseher, die Soundbar oder der A/V-Receiver ausschaltet oder der Eingang gewechselt wird, sollte die Wiedergabe in der App beendet werden.
- und alle anderen externen Prozesse, die die Wiedergabe beeinflussen müssen.
Das ist für viele Anwendungsfälle ideal. Insbesondere sollten Sie MediaSession
in folgenden Fällen verwenden:
- Sie streamen Videoinhalte im Langformat, z. B. Filme oder Live-TV.
- Sie streamen Audioinhalte im Langformat, z. B. Podcasts oder Musikplaylists.
- Sie entwickeln eine TV-App.
Nicht alle Anwendungsfälle passen jedoch gut zur MediaSession
. In den folgenden Fällen sollten Sie nur die Player
verwenden:
- Sie präsentieren kurze Inhalte, bei denen das Nutzer-Engagement und die Interaktion entscheidend sind.
- Es wird kein einzelnes aktives Video wiedergegeben, z. B. wenn der Nutzer durch eine Liste scrollt und mehrere Videos gleichzeitig auf dem Bildschirm angezeigt werden.
- Sie spielen ein einmaliges Einführungs- oder Erklärvideo ab, das sich Nutzer aktiv ansehen sollen.
- Deine Inhalte sind datenschutzrelevant und du möchtest nicht, dass externe Prozesse auf die Medienmetadaten zugreifen (z. B. im Inkognitomodus in einem Browser).
Wenn Ihr Anwendungsfall nicht zu den oben aufgeführten Anwendungsfällen passt, überlegen Sie, ob Sie damit einverstanden sind, dass die Wiedergabe in Ihrer App fortgesetzt wird, wenn der Nutzer nicht aktiv mit den Inhalten interagiert. Wenn die Antwort ja lautet, sollten Sie wahrscheinlich MediaSession
auswählen. Ist das nicht der Fall, sollten Sie stattdessen Player
verwenden.
Mediensitzung erstellen
Eine Mediensitzung existiert parallel zum Player, den sie verwaltet. Du kannst eine Mediensitzung mit einem Context
- und einem Player
-Objekt erstellen. Du solltest eine Mediensitzung bei Bedarf erstellen und initialisieren, z. B. mit der Lebenszyklusmethode onStart()
oder onResume()
der Activity
oder Fragment
oder der onCreate()
-Methode der Service
, zu der die Mediensitzung und der zugehörige Player gehören.
Um eine Mediensitzung zu erstellen, initialisiere eine Player
und gib sie so an MediaSession.Builder
weiter:
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 Statusverwaltung
Die Media3-Bibliothek aktualisiert die Mediensitzung automatisch anhand des Status des Players. Du musst die Zuordnung von Spieler zu Sitzung also nicht manuell vornehmen.
Das ist ein Unterschied zum bisherigen Ansatz, bei dem du eine PlaybackStateCompat
unabhängig vom Player erstellen und verwalten musstest, 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 App nur eine Sitzungs-Instanz erstellen soll, was der häufigste Fall ist.
Wenn eine App mehrere Sitzungsinstanzen gleichzeitig verwalten möchte, muss die Sitzungs-ID jeder Sitzung eindeutig sein. Die Sitzungs-ID kann beim Erstellen der Sitzung mit MediaSession.Builder.setId(String id)
festgelegt werden.
Wenn IllegalStateException
Ihre App mit der Fehlermeldung IllegalStateException: Session ID must be unique. ID=
zum Absturz bringt, wurde wahrscheinlich eine Sitzung unerwartet 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 Auslösen einer Ausnahme benachrichtigt.
Andere Kunden verwalten lassen
Die Mediensitzung ist der Schlüssel zur Steuerung der Wiedergabe. Sie können damit Befehle von externen Quellen an den Player weiterleiten, der Ihre Medien abspielt. Das können physische Tasten wie die Wiedergabetaste auf einem Headset oder einer Fernsehfernbedienung oder indirekte Befehle wie „Pause“ an Google Assistant sein. Ebenso können Sie Zugriff auf das Android-System gewähren, um die Benachrichtigungs- und Sperrbildschirmsteuerung zu vereinfachen, oder auf eine Wear OS-Smartwatch, damit Sie die Wiedergabe über das Zifferblatt steuern können. Externe Clients können über einen Mediacontroller Wiedergabebefehle an Ihre Medien-App senden. Diese werden von Ihrer Mediensitzung empfangen, die die Befehle an den Mediaplayer weiterleitet.
Wenn ein Controller eine Verbindung zu deiner Mediensitzung herstellen möchte, wird die Methode onConnect()
aufgerufen. Mithilfe des bereitgestellten ControllerInfo
können Sie entscheiden, ob Sie die Anfrage annehmen oder ablehnen. Ein Beispiel für die Annahme einer Verbindungsanfrage findest du im Abschnitt Verfügbare Befehle angeben.
Nach der Verbindung kann ein Controller Wiedergabebefehle an die Sitzung senden. Die Sitzung delegiert diese Befehle dann an den Player. Wiedergabe- und Playlistbefehle, die in der Player
-Benutzeroberfläche definiert sind, werden automatisch von der Sitzung verarbeitet.
Mit anderen Rückrufmethoden kannst du beispielsweise Anfragen für benutzerdefinierte Wiedergabebefehle und Änderungen an der Playlist verarbeiten.
Diese Callbacks enthalten ebenfalls ein ControllerInfo
-Objekt, sodass Sie die Reaktion auf jede Anfrage pro Controller ändern können.
Playlist ändern
Eine Mediensitzung kann die Playlist des Players direkt ändern, wie im ExoPlayer-Leitfaden für Playlists beschrieben.
Mit einem Controller kann die Playlist auch geändert werden, wenn entweder COMMAND_SET_MEDIA_ITEM
oder COMMAND_CHANGE_MEDIA_ITEMS
für den Controller verfügbar ist.
Wenn du der Playlist neue Elemente hinzufügst, benötigt der Player in der Regel MediaItem
-Instanzen mit einem definierten URI, damit sie abgespielt werden können. Neue Elemente werden standardmäßig automatisch an Playermethoden wie player.addMediaItem
weitergeleitet, wenn für sie ein URI definiert ist.
Wenn du die MediaItem
-Instanzen anpassen möchtest, die dem Player hinzugefügt wurden, kannst du onAddMediaItems()
überschreiben.
Dieser Schritt ist erforderlich, wenn du Controller unterstützen möchtest, die Medien ohne definierten URI anfordern. Stattdessen sind in der MediaItem
normalerweise mindestens eines der folgenden Felder festgelegt, um die angeforderten Medien zu beschreiben:
MediaItem.id
: Eine generische ID, die die Medien identifiziert.MediaItem.RequestMetadata.mediaUri
: Ein Anfrage-URI, der ein benutzerdefiniertes Schema verwenden kann und nicht unbedingt direkt vom Player abgespielt werden kann.MediaItem.RequestMetadata.searchQuery
: Eine textbasierte Suchanfrage, z. B. von Google Assistant.MediaItem.MediaMetadata
: Strukturierte Metadaten wie „Titel“ oder „Künstler“.
Wenn du noch mehr Anpassungsoptionen für ganz neue Playlists nutzen möchtest, kannst du zusätzlich onSetMediaItems()
überschreiben. Damit kannst du das Startelement und die Position in der Playlist festlegen. Du kannst beispielsweise ein einzelnes angefordertes Element zu einer ganzen Playlist erweitern und den Player anweisen, am Index des ursprünglich angeforderten Elements zu beginnen. Eine Beispielimplementierung von onSetMediaItems()
mit dieser Funktion finden Sie in der Demo-App für die Sitzung.
Benutzerdefiniertes Layout und benutzerdefinierte Befehle verwalten
In den folgenden Abschnitten wird beschrieben, wie Sie Client-Apps ein benutzerdefiniertes Layout von Schaltflächen für benutzerdefinierte Befehle anzeigen und Controller zum Senden der benutzerdefinierten Befehle autorisieren.
Benutzerdefiniertes Layout der Sitzung definieren
Wenn du Client-Apps angeben möchtest, welche Wiedergabesteuerungen dem Nutzer angezeigt werden sollen, musst du das benutzerdefinierte Layout der Sitzung festlegen, wenn du die MediaSession
in der onCreate()
-Methode deines Dienstes erstellst.
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 angeben
Medienanwendungen können benutzerdefinierte Befehle definieren, die beispielsweise in einem benutzerdefinierten Layout verwendet werden können. Sie können beispielsweise Schaltflächen implementieren, mit denen Nutzer ein Medienelement in einer Liste der Favoriten speichern können. Die MediaController
sendet benutzerdefinierte Befehle und die MediaSession.Callback
empfängt sie.
Du kannst festlegen, welche benutzerdefinierten Sitzungsbefehle für eine MediaController
verfügbar sind, wenn sie eine Verbindung zu deiner Mediensitzung herstellt. Dazu müssen Sie MediaSession.Callback.onConnect()
überschreiben. Konfiguriere und gib die verfügbaren Befehle zurück, wenn du in der onConnect
-Callback-Methode eine Verbindungsanfrage von einem MediaController
akzeptierst:
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 du benutzerdefinierte Befehlsanfragen von einer MediaController
erhalten möchtest, überschreibe die Methode onCustomCommand()
in der 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) ); } ... } }
Du kannst mithilfe der packageName
-Property des MediaSession.ControllerInfo
-Objekts, das an Callback
-Methoden übergeben wird, nachverfolgen, welcher Mediacontroller eine Anfrage stellt. So können Sie das Verhalten Ihrer App auf einen bestimmten Befehl anpassen, wenn er vom System, Ihrer eigenen App oder anderen Client-Apps stammt.
Benutzerdefiniertes Layout nach einer Nutzerinteraktion aktualisieren
Nachdem du einen benutzerdefinierten Befehl oder eine andere Interaktion mit dem Player verarbeitet hast, kannst du das Layout aktualisieren, das in der Benutzeroberfläche des Controllers angezeigt wird. Ein typisches Beispiel ist eine Ein-/Aus-Schaltfläche, deren Symbol sich ändert, nachdem die mit dieser Schaltfläche verknüpfte 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
-Benutzeroberfläche definierten Befehls anpassen möchten, z. B. play()
oder seekToNext()
, setzen Sie Player
in ForwardingSimpleBasePlayer
ein, bevor Sie ihn an MediaSession
übergeben.
Kotlin
val player = (logic to build a Player instance) val forwardingPlayer = object : ForwardingSimpleBasePlayer(player) { // Customizations } val mediaSession = MediaSession.Builder(context, forwardingPlayer).build()
Java
ExoPlayer player = (logic to build a Player instance) ForwardingSimpleBasePlayer forwardingPlayer = new ForwardingSimpleBasePlayer(player) { // Customizations }; MediaSession mediaSession = new MediaSession.Builder(context, forwardingPlayer).build();
Weitere Informationen zu ForwardingSimpleBasePlayer
findest du im ExoPlayer-Leitfaden zur Anpassung.
Anfordernden Controller eines Player-Befehls identifizieren
Wenn ein Aufruf einer Player
-Methode von einer MediaController
ausgeht, kannst du die Quelle mit MediaSession.controllerForCurrentRequest
identifizieren und die ControllerInfo
für die aktuelle Anfrage abrufen:
Kotlin
class CallerAwarePlayer(player: Player) : ForwardingSimpleBasePlayer(player) { override fun handleSeek( mediaItemIndex: Int, positionMs: Long, seekCommand: Int, ): ListenableFuture<*> { Log.d( "caller", "seek operation from package ${session.controllerForCurrentRequest?.packageName}", ) return super.handleSeek(mediaItemIndex, positionMs, seekCommand) } }
Java
public class CallerAwarePlayer extends ForwardingSimpleBasePlayer { public CallerAwarePlayer(Player player) { super(player); } @Override protected ListenableFuture<?> handleSeek( int mediaItemIndex, long positionMs, int seekCommand) { Log.d( "caller", "seek operation from package: " + session.getControllerForCurrentRequest().getPackageName()); return super.handleSeek(mediaItemIndex, positionMs, seekCommand); } }
Auf Medienschaltflächen reagieren
Medienschaltflächen sind Hardwareschaltflächen auf Android-Geräten und anderen Peripheriegeräten, z. B. die Wiedergabe-/Pause-Taste auf einem Bluetooth-Headset. Media3 verarbeitet Medienschalter-Ereignisse für dich, wenn sie in der Sitzung eintreffen, und ruft die entsprechende Player
-Methode auf dem Sitzungsplayer auf.
Eine App kann das Standardverhalten überschreiben, indem sie MediaSession.Callback.onMediaButtonEvent(Intent)
überschreibt. In einem solchen Fall kann oder muss die App alle API-spezifischen Anforderungen selbst verarbeiten.
Fehlerbehandlung und -berichte
Es gibt zwei Arten von Fehlern, die von einer Sitzung ausgegeben und an Controller gemeldet werden. Schwerwiegende Fehler geben einen technischen Wiedergabefehler des Sitzungsplayers an, der die Wiedergabe unterbricht. Schwerwiegende Fehler werden dem Controller automatisch gemeldet, wenn sie auftreten. Nicht schwerwiegende Fehler sind nicht technische Fehler oder Richtlinienverstöße, die die Wiedergabe nicht unterbrechen und von der Anwendung manuell an Controller gesendet werden.
Schwerwiegende Wiedergabefehler
Ein schwerwiegender Wiedergabefehler wird vom Player an die Sitzung und dann an die Controller gemeldet, die Player.Listener.onPlayerError(PlaybackException)
und Player.Listener.onPlayerErrorChanged(@Nullable PlaybackException)
aufrufen.
In diesem Fall wird der Wiedergabestatus in STATE_IDLE
geändert und MediaController.getPlaybackError()
gibt die PlaybackException
zurück, die den Übergang verursacht hat. Ein Controller kann die PlayerException.errorCode
prüfen, um Informationen zum Grund des Fehlers zu erhalten.
Für die Interoperabilität wird ein schwerwiegender Fehler an den PlaybackStateCompat
der Plattformsitzung repliziert, indem sein Status in STATE_ERROR
geändert und der Fehlercode und die Meldung gemäß der PlaybackException
festgelegt werden.
Anpassung eines schwerwiegenden Fehlers
Um dem Nutzer lokalisierte und aussagekräftige Informationen zur Verfügung zu stellen, können der Fehlercode, die Fehlermeldung und die Fehler-Extras eines schwerwiegenden Wiedergabefehlers angepasst werden. Dazu wird beim Erstellen der Sitzung ein ForwardingPlayer
verwendet:
Kotlin
val forwardingPlayer = ErrorForwardingPlayer(player) val session = MediaSession.Builder(context, forwardingPlayer).build()
Java
Player forwardingPlayer = new ErrorForwardingPlayer(player); MediaSession session = new MediaSession.Builder(context, forwardingPlayer).build();
Der weiterleitende Player registriert eine Player.Listener
beim tatsächlichen Player und unterbricht Callbacks, die einen Fehler melden. Eine benutzerdefinierte PlaybackException
wird dann an die Listener delegiert, die beim weiterleitenden Player registriert sind. Damit das funktioniert, überschreibt der weiterleitende Player Player.addListener
und Player.removeListener
, um Zugriff auf die Listener zu erhalten, über die ein benutzerdefinierter Fehlercode, eine Nachricht oder zusätzliche Informationen gesendet werden können:
Kotlin
class ErrorForwardingPlayer(private val context: Context, player: Player) : ForwardingPlayer(player) { private val listeners: MutableList<Player.Listener> = mutableListOf() private var customizedPlaybackException: PlaybackException? = null init { player.addListener(ErrorCustomizationListener()) } override fun addListener(listener: Player.Listener) { listeners.add(listener) } override fun removeListener(listener: Player.Listener) { listeners.remove(listener) } override fun getPlayerError(): PlaybackException? { return customizedPlaybackException } private inner class ErrorCustomizationListener : Player.Listener { override fun onPlayerErrorChanged(error: PlaybackException?) { customizedPlaybackException = error?.let { customizePlaybackException(it) } listeners.forEach { it.onPlayerErrorChanged(customizedPlaybackException) } } override fun onPlayerError(error: PlaybackException) { listeners.forEach { it.onPlayerError(customizedPlaybackException!!) } } private fun customizePlaybackException( error: PlaybackException, ): PlaybackException { val buttonLabel: String val errorMessage: String when (error.errorCode) { PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW -> { buttonLabel = context.getString(R.string.err_button_label_restart_stream) errorMessage = context.getString(R.string.err_msg_behind_live_window) } // Apps can customize further error messages by adding more branches. else -> { buttonLabel = context.getString(R.string.err_button_label_ok) errorMessage = context.getString(R.string.err_message_default) } } val extras = Bundle() extras.putString("button_label", buttonLabel) return PlaybackException(errorMessage, error.cause, error.errorCode, extras) } override fun onEvents(player: Player, events: Player.Events) { listeners.forEach { it.onEvents(player, events) } } // Delegate all other callbacks to all listeners without changing arguments like onEvents. } }
Java
private static class ErrorForwardingPlayer extends ForwardingPlayer { private final Context context; private List<Player.Listener> listeners; @Nullable private PlaybackException customizedPlaybackException; public ErrorForwardingPlayer(Context context, Player player) { super(player); this.context = context; listeners = new ArrayList<>(); player.addListener(new ErrorCustomizationListener()); } @Override public void addListener(Player.Listener listener) { listeners.add(listener); } @Override public void removeListener(Player.Listener listener) { listeners.remove(listener); } @Nullable @Override public PlaybackException getPlayerError() { return customizedPlaybackException; } private class ErrorCustomizationListener implements Listener { @Override public void onPlayerErrorChanged(@Nullable PlaybackException error) { customizedPlaybackException = error != null ? customizePlaybackException(error, context) : null; for (int i = 0; i < listeners.size(); i++) { listeners.get(i).onPlayerErrorChanged(customizedPlaybackException); } } @Override public void onPlayerError(PlaybackException error) { for (int i = 0; i < listeners.size(); i++) { listeners.get(i).onPlayerError(checkNotNull(customizedPlaybackException)); } } private PlaybackException customizePlaybackException( PlaybackException error, Context context) { String buttonLabel; String errorMessage; switch (error.errorCode) { case PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW: buttonLabel = context.getString(R.string.err_button_label_restart_stream); errorMessage = context.getString(R.string.err_msg_behind_live_window); break; // Apps can customize further error messages by adding more case statements. default: buttonLabel = context.getString(R.string.err_button_label_ok); errorMessage = context.getString(R.string.err_message_default); break; } Bundle extras = new Bundle(); extras.putString("button_label", buttonLabel); return new PlaybackException(errorMessage, error.getCause(), error.errorCode, extras); } @Override public void onEvents(Player player, Events events) { for (int i = 0; i < listeners.size(); i++) { listeners.get(i).onEvents(player, events); } } // Delegate all other callbacks to all listeners without changing arguments like onEvents. } }
Nicht kritische Fehler
Nicht kritische Fehler, die nicht auf eine technische Ausnahme zurückzuführen sind, können von einer App an alle oder an einen bestimmten Controller gesendet werden:
Kotlin
val sessionError = SessionError( SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED, context.getString(R.string.error_message_authentication_expired), ) // Sending a nonfatal error to all controllers. mediaSession.sendError(sessionError) // Interoperability: Sending a nonfatal error to the media notification controller to set the // error code and error message in the playback state of the platform media session. mediaSession.mediaNotificationControllerInfo?.let { mediaSession.sendError(it, sessionError) }
Java
SessionError sessionError = new SessionError( SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED, context.getString(R.string.error_message_authentication_expired)); // Sending a nonfatal error to all controllers. mediaSession.sendError(sessionError); // Interoperability: Sending a nonfatal error to the media notification controller to set the // error code and error message in the playback state of the platform media session. ControllerInfo mediaNotificationControllerInfo = mediaSession.getMediaNotificationControllerInfo(); if (mediaNotificationControllerInfo != null) { mediaSession.sendError(mediaNotificationControllerInfo, sessionError); }
Ein nicht fataler Fehler, der an den Media Notification Controller gesendet wird, wird in die PlaybackStateCompat
der Plattformsitzung repliziert. Dabei werden nur der Fehlercode und die Fehlermeldung entsprechend auf PlaybackStateCompat
festgelegt, während PlaybackStateCompat.state
nicht in STATE_ERROR
geändert wird.
Nicht schwerwiegende Fehler erhalten
Ein MediaController
erhält einen nicht schwerwiegenden Fehler, wenn MediaController.Listener.onError
implementiert wird:
Kotlin
val future = MediaController.Builder(context, sessionToken) .setListener(object : MediaController.Listener { override fun onError(controller: MediaController, sessionError: SessionError) { // Handle nonfatal error. } }) .buildAsync()
Java
MediaController.Builder future = new MediaController.Builder(context, sessionToken) .setListener( new MediaController.Listener() { @Override public void onError(MediaController controller, SessionError sessionError) { // Handle nonfatal error. } });