Controllare e pubblicizzare la riproduzione utilizzando una MediaSession

Le sessioni multimediali offrono un modo universale di interagire con un audio o un video un player. In Media3, il player predefinito è la classe ExoPlayer, che implementa l'interfaccia di Player. La connessione della sessione multimediale al player consente a un'app per pubblicizzare la riproduzione di contenuti multimediali esternamente e per ricevere comandi di riproduzione dalle da fonti esterne.

I comandi possono provenire da pulsanti fisici come il pulsante di riproduzione su un cuffie o telecomando della TV. Potrebbero anche provenire da app client che hanno un controller multimediale, ad esempio mettendo in pausa all'Assistente Google. I contenuti multimediali sessione delega questi comandi al player dell'app multimediale.

Quando scegliere una sessione multimediale

Quando implementi MediaSession, consenti agli utenti di controllare la riproduzione:

  • Tramite le cuffie. Spesso ci sono interazioni di pulsanti o tocco l'utente può eseguire sulle cuffie per riprodurre o mettere in pausa i contenuti multimediali oppure per passare o traccia precedente.
  • Parlando con l'Assistente Google. Spesso si dice "OK" Google, metti in pausa" per mettere in pausa tutti i contenuti multimediali attualmente in riproduzione sul dispositivo.
  • Tramite il suo smartwatch Wear OS. Ciò consente di accedere più facilmente i controlli di riproduzione più comuni mentre gioca sullo smartphone.
  • Tramite i Controlli multimediali. Questo carosello mostra i controlli per ogni sessione multimediale in esecuzione.
  • Sulla TV. Consente azioni con pulsanti di riproduzione fisici e riproduzione piattaforma e la gestione dell'alimentazione (ad esempio, se la TV, la soundbar o il ricevitore A/V viene disattivata o l'ingresso viene cambiato, la riproduzione dovrebbe interrompersi nell'app).
  • E qualsiasi altro processo esterno che debba influenzare la riproduzione.

È un ottimo modo per molti casi d'uso. In particolare, devi considerare attentamente utilizzo di MediaSession quando:

  • Stai trasmettendo in streaming contenuti video nel formato lungo, come film o TV in diretta.
  • Stai trasmettendo in streaming contenuti audio di durata estesa, ad esempio podcast o musica. playlist.
  • Stai creando un'app TV.

Tuttavia, non tutti i casi d'uso si adattano bene a MediaSession. Potresti voler utilizza solo Player nei seguenti casi:

  • Stai mostrando contenuti nel formato breve in cui il coinvolgimento e l'interazione degli utenti è fondamentale.
  • Non è presente un solo video attivo, ad esempio l'utente sta scorrendo un elenco. e vengono visualizzati più video contemporaneamente sullo schermo.
  • Stai riproducendo un video introduttivo o esplicativo, che si aspettano che l'utente guardi attivamente.
  • I tuoi contenuti sono riservati alla privacy e non vuoi che i processi esterni Accedere ai metadati multimediali (ad esempio la modalità di navigazione in incognito in un browser)

Se il tuo caso d'uso non rientra tra quelli elencati sopra, valuta se stai Ok che la riproduzione dell'app continui anche quando l'utente non sta interagendo attivamente con i contenuti. Se la risposta è sì, probabilmente vorrai scegliere MediaSession. Se la risposta è no, probabilmente ti consigliamo di utilizzare lo strumento Player .

Creare una sessione multimediale

Una sessione multimediale si trova accanto al player che gestisce. Puoi creare un sessione multimediale con un oggetto Context e un Player. Devi creare e gestire inizializzare una sessione multimediale quando è necessario, ad esempio onStart() o onResume() metodo del ciclo di vita di Activity, Fragment o onCreate() del Service proprietario della sessione multimediale e del player associato.

Per creare una sessione multimediale, inizializza un Player e forniscilo a MediaSession.Builder nel seguente modo:

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();

Gestione automatica dello stato

La libreria Media3 aggiorna automaticamente la sessione multimediale utilizzando lo stato del player. Di conseguenza, non è necessario gestire manualmente il mapping player alla sessione.

Si tratta di una interruzione dall'approccio legacy in cui dovevi creare e mantenere a PlaybackStateCompat indipendentemente dal player stesso, ad esempio per indicare eventuali errori.

ID sessione univoco

Per impostazione predefinita, MediaSession.Builder crea una sessione con una stringa vuota come l'ID sessione. È sufficiente se un'app intende creare solo un singolo di sessione, che è il caso più comune.

Se un'app vuole gestire contemporaneamente più istanze di sessione, deve garantire che l'ID sessione di ogni sessione sia univoco. L'ID sessione può da impostare durante la creazione della sessione con MediaSession.Builder.setId(String id).

Se vedi un IllegalStateException che arresta l'arresto anomalo della tua app con l'errore messaggio IllegalStateException: Session ID must be unique. ID=, allora è probabile che una sessione sia stata creata in modo imprevisto prima di una è stata rilasciata un'istanza con lo stesso ID. Per evitare che le sessioni vengano divulgate da un un errore di programmazione, questi casi vengono rilevati e notificati con un messaggio .

Concedi il controllo ad altri clienti

La sessione multimediale è la chiave per controllare la riproduzione. Ti permette di indirizzare da fonti esterne al player che si occupa di riprodurre i tuoi contenuti multimediali. Queste sorgenti possono essere pulsanti fisici, come il pulsante di riproduzione su cuffie o telecomando della TV oppure comandi indiretti come "metti in pausa" all'Assistente Google. Analogamente, potresti voler concedere l'accesso ai per agevolare i controlli delle notifiche e della schermata di blocco o su Wear OS per controllare la riproduzione dal quadrante orologio. I client esterni possono usa un controller multimediale per impartire comandi di riproduzione alla tua app multimediale. Si tratta di ricevuta dalla sessione multimediale, che alla fine delega i comandi alla lettore multimediale.

Diagramma che mostra l'interazione tra MediaSession e MediaController.
Figura 1: il controller multimediale facilita il passaggio da fonti esterne alla sessione multimediale.
di Gemini Advanced.

Quando un controller sta per connettersi alla tua sessione multimediale, onConnect() . Puoi utilizzare il ControllerInfo fornito decidere se accettare o rifiuta la richiesta. Per un esempio di accettazione di una richiesta di collegamento, vedi la sezione Dichiarazione disponibili.

Dopo la connessione, un controller può inviare comandi di riproduzione alla sessione. La poi delega questi comandi al player. Riproduzione e playlist I comandi definiti nell'interfaccia Player vengono gestiti automaticamente durante la sessione.

Altri metodi di callback ti consentono di gestire, ad esempio, le richieste di comandi di riproduzione personalizzati e modificando la playlist). Questi callback includono in modo simile un oggetto ControllerInfo che puoi modificare il modo in cui rispondi a ogni richiesta in base al titolare.

Modificare la playlist

Una sessione multimediale può modificare direttamente la playlist del relativo player come spiegato in il Guida di ExoPlayer per le playlist. I controller possono anche modificare la playlist se: COMMAND_SET_MEDIA_ITEM o COMMAND_CHANGE_MEDIA_ITEMS sia disponibile per il controller.

Quando aggiungi nuovi elementi alla playlist, il player in genere richiede MediaItem con un URI definito per renderli riproducibili. Per impostazione predefinita, gli elementi appena aggiunti vengono inoltrati automaticamente ai metodi del player come player.addMediaItem se hanno un URI definito.

Se vuoi personalizzare le istanze MediaItem aggiunte al player, puoi: eseguire l'override onAddMediaItems() Questo passaggio è necessario se vuoi supportare i controller che richiedono contenuti multimediali senza un URI definito. Invece, MediaItem in genere ha uno o più dei seguenti campi impostati per descrivere l'elemento multimediale richiesto:

  • MediaItem.id: un ID generico che identifica i contenuti multimediali.
  • MediaItem.RequestMetadata.mediaUri: un URI della richiesta che può utilizzare una richiesta e non è necessariamente riproducibile direttamente dal player.
  • MediaItem.RequestMetadata.searchQuery: una query di ricerca testuale, ad esempio dall'Assistente Google.
  • MediaItem.MediaMetadata: metadati strutturati come "title" o "artista".

Per ulteriori opzioni di personalizzazione per playlist completamente nuove, puoi sostituire ulteriormente onSetMediaItems() che ti consente di definire l'elemento iniziale e la posizione nella playlist. Ad esempio: puoi espandere un singolo elemento richiesto a un'intera playlist e indicare al player in modo che inizi dall'indice dell'elemento originariamente richiesto. R implementazione di esempio di onSetMediaItems() disponibili nell'app demo della sessione.

Gestire il layout e i comandi personalizzati

Le seguenti sezioni descrivono come pubblicizzare un layout personalizzato i pulsanti di comando alle app client e autorizza i controller a inviare tramite comandi SQL.

Definisci il layout personalizzato della sessione

Per indicare alle app client i controlli di riproduzione che vuoi mostrare ai L'utente, imposta il layout personalizzato della sessione quando crei MediaSession nel metodo onCreate() della tua completamente gestito di Google Cloud.

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

Dichiara i comandi personalizzati e del player disponibili

Le applicazioni multimediali possono definire comandi personalizzati che, ad esempio, possono essere utilizzati un layout personalizzato. Ad esempio, potresti voler implementare pulsanti che consentano alla utente per salvare un elemento multimediale in un elenco di elementi preferiti. MediaController invia comandi personalizzati e l'MediaSession.Callback li riceve.

Puoi definire quali comandi di sessione personalizzati rendere disponibili MediaController quando si connette alla sessione multimediale. Per farlo, eseguendo l'override di MediaSession.Callback.onConnect(). Configura e restituisci l'insieme di comandi disponibili quando si accetta una richiesta di connessione da un MediaController nel metodo di callback onConnect:

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

Per ricevere richieste di comandi personalizzati da un MediaController, esegui l'override di 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)
      );
    }
    ...
  }
}

Puoi tenere traccia del controller multimediale che sta effettuando una richiesta utilizzando il comando Proprietà packageName dell'oggetto MediaSession.ControllerInfo che è passati ai metodi Callback. Questo ti consente di personalizzare comportamento in risposta a un dato comando, se proviene dal sistema, della tua app o di altre app client.

Aggiornare il layout personalizzato dopo un'interazione dell'utente

Dopo aver gestito un comando personalizzato o qualsiasi altra interazione con il player, potrebbe voler aggiornare il layout visualizzato nell'interfaccia utente del controller. Un esempio tipico è un pulsante di attivazione/disattivazione che cambia la propria icona dopo aver attivato l'azione associata con questo pulsante. Per aggiornare il layout, puoi utilizzare MediaSession.setCustomLayout:

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

Personalizza il comportamento del comando di riproduzione

Per personalizzare il comportamento di un comando definito nell'interfaccia Player, ad esempio come play() o seekToNext(), racchiudi Player in un ForwardingPlayer.

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();

Per ulteriori informazioni su ForwardingPlayer, consulta la guida di ExoPlayer su Personalizzazione.

Identifica il controller che richiede un comando del player

Quando una chiamata a un metodo Player proviene da un MediaController, puoi identificare la fonte di origine con MediaSession.controllerForCurrentRequest e acquisisci ControllerInfo per la richiesta corrente:

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

Rispondi ai pulsanti multimediali

I pulsanti multimediali sono pulsanti hardware presenti su dispositivi Android e altre periferiche dispositivi, ad esempio il pulsante di riproduzione/pausa sulle cuffie Bluetooth. Handle Media3 degli eventi del pulsante multimediale quando arrivano alla sessione e chiamano metodo Player appropriato nel player della sessione.

Un'app può eseguire l'override del comportamento predefinito MediaSession.Callback.onMediaButtonEvent(Intent). In questo caso, l'app gestire tutte le specifiche delle API in autonomia.

Gestione e reporting degli errori

Esistono due tipi di errori generati da una sessione e segnalati ai controller. Gli errori irreversibili segnalano un errore tecnico di riproduzione della sessione player che interrompe la riproduzione. Gli errori irreversibili vengono segnalati al controller automaticamente quando si verificano. Gli errori non irreversibili non sono tecnici o normativi errori che non interrompono la riproduzione e che vengono inviati ai controller manualmente l'applicazione.

Errori di riproduzione irreversibili

Il giocatore segnala un errore di riproduzione irreversibile alla sessione, dopodiché segnalato ai controller di chiamare tramite Player.Listener.onPlayerError(PlaybackException) e Player.Listener.onPlayerErrorChanged(@Nullable PlaybackException).

In questo caso, lo stato di riproduzione viene trasferito a STATE_IDLE e MediaController.getPlaybackError() restituisce PlaybackException che ha causato la transizione. Un controller può ispezionare PlayerException.errorCode per ottenere le informazioni sul motivo dell'errore.

Per l'interoperabilità, un errore irreversibile viene replicato nell'PlaybackStateCompat della sessione della piattaforma eseguendo la transizione dello stato a STATE_ERROR e impostando codice di errore e il messaggio in base a PlaybackException.

Personalizzazione di un errore irreversibile

Per fornire all'utente informazioni localizzate e significative, il codice di errore, il messaggio di errore e gli elementi extra relativi a un errore di riproduzione irreversibile possono essere personalizzati utilizzando ForwardingPlayer durante la creazione della sessione:

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();

Il player di inoltro registra un Player.Listener nel player effettivo e intercetta i callback che segnalano un errore. Un modello personalizzato PlaybackException viene quindi delegato ai listener che sono registrati nel player di inoltro. Affinché questa operazione funzioni, il player di inoltro esegue l'override di Player.addListener e Player.removeListener per avere accesso listener con cui inviare codici di errore, messaggi o extra personalizzati:

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

Errori non irreversibili

È possibile inviare errori non irreversibili che non derivano da un'eccezione tecnica da un'app a tutti o a un controller specifico:

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

Un errore non irreversibile inviato al controller di notifica multimediale viene replicato nel PlaybackStateCompat della sessione sulla piattaforma. Di conseguenza, solo il codice di errore e il messaggio di errore viene impostato di conseguenza su PlaybackStateCompat, PlaybackStateCompat.state non modificato in STATE_ERROR.

Ricevi errori non irreversibili

Un MediaController riceve un errore non irreversibile tramite l'implementazione MediaController.Listener.onError:

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