Wiedergabe mit einer MediaSession steuern und bewerben

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äufiges 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 Wiedergabeschaltflächen, 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 zeigen kurze Inhalte, für die keine externe Steuerung oder Hintergrundwiedergabe erforderlich ist.
  • 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, ohne externe Wiedergabesteuerungen zu benötigen.
  • 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 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.

Wenn du eine Mediensitzung erstellen möchtest, 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 unterscheidet sich von der Plattformmediensitzung, bei der du eine PlaybackState unabhängig vom Player erstellen und verwalten musstest, z. B. um Fehler anzugeben.

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 es wird eine Ausnahme ausgelöst.

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.

Ein Diagramm, das die Interaktion zwischen einer MediaSession und einer MediaController veranschaulicht.
Abbildung 1: Der Mediencontroller ermöglicht die Weitergabe von Befehlen von externen Quellen an die Mediensitzung.

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. Standardmäßig werden neu hinzugefügte Elemente 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 einen einzelnen angeforderten Titel zu einer ganzen Playlist erweitern und den Player anweisen, am Index des ursprünglich angeforderten Titels 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 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 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/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 oder Richtlinienfehler, 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 in der Plattformsitzung repliziert, indem der Status in STATE_ERROR geändert und der Fehlercode und die Meldung gemäß der PlaybackException festgelegt werden.

Anpassung von schwerwiegenden Fehlern

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 kann ForwardingSimpleBasePlayer verwenden, um den Fehler abzufangen und den Fehlercode, die Meldung oder die Extras anzupassen. Auf die gleiche Weise kannst du auch neue Fehler generieren, die im ursprünglichen Player nicht vorhanden sind:

Kotlin

class ErrorForwardingPlayer (private val context: Context, player: Player) :
    ForwardingSimpleBasePlayer(player) {

  override fun getState(): State {
    var state = super.getState()
    if (state.playerError != null) {
      state =
        state.buildUpon()
          .setPlayerError(customizePlaybackException(state.playerError!!))
          .build()
    }
    return state
  }

  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)
      }
      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)
  }
}

Java

class ErrorForwardingPlayer extends ForwardingSimpleBasePlayer {

  private final Context context;

  public ErrorForwardingPlayer(Context context, Player player) {
    super(player);
    this.context = context;
  }

  @Override
  protected State getState() {
    State state = super.getState();
    if (state.playerError != null) {
      state =
          state.buildUpon()
              .setPlayerError(customizePlaybackException(state.playerError))
              .build();
    }
    return state;
  }

  private PlaybackException customizePlaybackException(PlaybackException error) {
    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;
      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);
  }
}

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),
)

// Option 1: Sending a nonfatal error to all controllers.
mediaSession.sendError(sessionError)

// Option 2: Sending a nonfatal error to the media notification controller only
// 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));

// Option 1: Sending a nonfatal error to all controllers.
mediaSession.sendError(sessionError);

// Option 2: Sending a nonfatal error to the media notification controller only
// 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);
}

Wenn ein nicht kritischer Fehler an den Controller für Medienbenachrichtigungen gesendet wird, werden der Fehlercode und die Fehlermeldung in der Mediensitzung der Plattform repliziert. PlaybackState.state wird dabei nicht in STATE_ERROR geändert.

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.
              }
            });