Contrôler et annoncer la lecture avec MediaSession

Les sessions multimédias fournissent un moyen universel d'interagir avec un contenu audio ou vidéo joueur. Dans Media3, le lecteur par défaut est la classe ExoPlayer, qui implémente l'interface Player. Connecter la session multimédia au lecteur permet à une application pour annoncer la lecture de contenus multimédias en externe et recevoir des commandes de lecture sources externes.

Les commandes peuvent provenir de boutons physiques, comme le bouton de lecture du casque ou de la télécommande du téléviseur. Elles peuvent également provenir d'applications clientes contrôleur multimédia, par exemple en demandant la commande "pause" à l'Assistant Google. Les médias délègue ces commandes au lecteur de l'application multimédia.

Quand choisir une session multimédia ?

Lorsque vous implémentez MediaSession, vous permettez aux utilisateurs de contrôler la lecture:

  • Via leur casque Il y a souvent des boutons ou des interactions tactiles l'utilisateur peut jouer ou mettre en pause un contenu multimédia avec son casque, ou passer au suivant ou la piste précédente.
  • En parlant à l'Assistant Google Une pratique courante consiste à dire "OK" "Google, mets en pause" pour mettre en pause tous les contenus multimédias en cours de lecture sur l'appareil.
  • Via sa montre Wear OS Cela vous permet d'accéder plus facilement commandes de lecture courantes lorsqu'ils jouent sur leur téléphone.
  • Via les commandes multimédias Ce carrousel présente des commandes pour chaque en cours d'exécution.
  • Sur un téléviseur Autorise les actions avec des boutons de lecture physiques et la lecture via la plate-forme et la gestion de l'alimentation (par exemple, si le téléviseur, la barre de son ou le récepteur AV ou si l'entrée change, la lecture doit s'arrêter dans l'application).
  • ainsi que tout autre processus externe qui doit influencer la lecture.

C'est la solution idéale dans de nombreux cas d'utilisation. En particulier, vous devez vivement envisager à l'aide de MediaSession lorsque:

  • Vous diffusez du contenu vidéo de longue durée, comme des films ou la télévision en direct.
  • Vous diffusez en streaming des contenus audio de longue durée, comme des podcasts ou de la musique playlists.
  • Vous développez une application TV.

Cependant, tous les cas d'utilisation ne sont pas adaptés à MediaSession. Vous voudrez peut-être Utilisez uniquement Player dans les cas suivants:

  • Vous diffusez des contenus courts, dans lesquels l'engagement et l'interaction des utilisateurs sont nombreux. est crucial.
  • n'a pas une seule vidéo active (par exemple, un utilisateur fait défiler une liste) ; et plusieurs vidéos s'affichent à la fois sur l'écran.
  • vous regardez une vidéo d'introduction ou d'explication ponctuelle, que l'utilisateur regarde activement.
  • Votre contenu est sensible à la confidentialité, et vous ne souhaitez pas que des processus externes Accéder aux métadonnées multimédias (par exemple, en mode navigation privée dans un navigateur)

Si votre cas d'utilisation ne correspond à aucun de ces critères, demandez-vous que votre application continue la lecture lorsque l'utilisateur n'interagit pas activement avec le contenu. Si la réponse est oui, vous souhaiterez probablement MediaSession Si la réponse est non, vous souhaiterez probablement utiliser la fonction Player à la place.

Créer une session multimédia

Les sessions multimédias accompagnent le lecteur qu'il gère. Vous pouvez construire un session multimédia avec un objet Context et un objet Player. Vous devez créer et d'initialiser une session multimédia si nécessaire, par exemple onStart() ou Méthode de cycle de vie onResume() de Activity, Fragment ou onCreate() Méthode du Service propriétaire de la session multimédia et du lecteur associé.

Pour créer une session multimédia, initialisez un Player et fournissez-le à MediaSession.Builder d'avis:

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

Gestion automatique de l'état

La bibliothèque Media3 met automatiquement à jour la session multimédia à l'aide de la méthode l'état du lecteur. Ainsi, vous n'avez pas besoin de gérer manuellement le mappage joueur à session.

Il s'agit d'une rupture par rapport à l'ancienne approche, où vous deviez créer et gérer un PlaybackStateCompat indépendamment du lecteur lui-même, par exemple pour indique d'éventuelles erreurs.

ID de session unique

Par défaut, MediaSession.Builder crée une session avec une chaîne vide en tant que l'ID de session. C'est suffisant si une application ne vise qu'une seule une instance de session, ce qui est le cas le plus courant.

Si une application souhaite gérer plusieurs instances de session en même temps, elle doit s'assurer que l'ID de session de chaque session est unique. L'ID de session peut doit être défini lors de la création de la session avec MediaSession.Builder.setId(String id).

Si vous constatez qu'une IllegalStateException plante votre application avec l'erreur message IllegalStateException: Session ID must be unique. ID= alors le résultat est probablement qu'une session ait été créée de manière inattendue avant qu'une session portant le même ID a été libérée. Pour éviter que les sessions soient divulguées par de programmation, de telles situations sont détectées et notifiées en renvoyant une une exception.

Accorder le contrôle à d'autres clients

La session multimédia est essentielle pour contrôler la lecture. Elle vous permet d'acheminer des commandes externes au lecteur qui effectue le travail de lecture de votre médias. Ces sources peuvent être des boutons physiques, comme le bouton de lecture de la télécommande du casque ou du téléviseur, ou des commandes indirectes telles que "Mettre en pause" à l'Assistant Google. De même, vous pouvez autoriser l'accès pour faciliter les commandes de notification et de verrouillage de l'écran, ou à un système d'exploitation Wear OS pour contrôler la lecture à partir du cadran. Les clients externes peuvent utiliser une télécommande multimédia pour envoyer des commandes de lecture à votre application multimédia. Il s'agit reçues par votre session multimédia, qui délègue ensuite des commandes au du lecteur multimédia.

Schéma illustrant l'interaction entre MediaSession et MediaController
Figure 1: Le contrôleur multimédia facilite la transmission à partir de sources externes vers la session multimédia.

Lorsqu'une manette est sur le point de se connecter à votre session multimédia, onConnect() est appelée. Vous pouvez utiliser la ControllerInfo fournie décider d'accepter ou non ou refuser la demande. Vous trouverez un exemple d'acceptation d'une demande de connexion dans la section Déclarer commandes disponibles.

Une fois connectée, une manette peut envoyer des commandes de lecture à la session. La délègue ensuite ces commandes au joueur. Lecture et playlist définies dans l'interface Player sont automatiquement gérées par session.

D'autres méthodes de rappel vous permettent, par exemple, de gérer les requêtes pour commandes de lecture personnalisées modifier la playlist). Ces rappels incluent également un objet ControllerInfo afin que vous puissiez modifier comment vous répondez à chaque demande pour chaque responsable du traitement.

Modifier la playlist

Une session multimédia peut modifier directement la playlist de son lecteur, comme expliqué dans la Guide ExoPlayer pour les playlists Les contrôleurs peuvent également modifier la playlist si : COMMAND_SET_MEDIA_ITEM ou COMMAND_CHANGE_MEDIA_ITEMS est disponible pour la manette.

Lors de l'ajout de nouveaux éléments à la playlist, le lecteur a généralement besoin de MediaItem des instances avec un URI défini pour les rendre jouables. Par défaut, les nouveaux éléments sont transférés automatiquement aux méthodes du lecteur telles que player.addMediaItem si un URI est défini.

Si vous souhaitez personnaliser les instances MediaItem ajoutées au lecteur, vous pouvez ignorer onAddMediaItems() Cette étape est nécessaire si vous souhaitez accepter les manettes qui demandent des contenus multimédias sans URI défini. Au lieu de cela, MediaItem a généralement Un ou plusieurs des champs suivants sont définis pour décrire le contenu multimédia demandé:

  • MediaItem.id: ID générique identifiant le contenu multimédia.
  • MediaItem.RequestMetadata.mediaUri: URI de requête pouvant utiliser un nom personnalisé et n'est pas nécessairement lisible directement par le lecteur.
  • MediaItem.RequestMetadata.searchQuery: requête de recherche textuelle, par exemple depuis l'Assistant Google.
  • MediaItem.MediaMetadata: métadonnées structurées telles que "title" ou "artist" (artiste).

Si vous souhaitez plus d'options de personnalisation pour de toutes nouvelles playlists, vous pouvez remplacer également onSetMediaItems() qui vous permet de définir l'élément de départ et la position dans la playlist. Par exemple, vous pouvez étendre un élément demandé à une playlist entière et demander à pour que le lecteur commence à l'index de l'élément initialement demandé. A Exemple d'implémentation de onSetMediaItems() avec cette fonctionnalité sont disponibles dans l'application de démonstration de la session.

Gérer la mise en page personnalisée et les commandes personnalisées

Les sections suivantes décrivent comment promouvoir une mise en page personnalisée aux applications clientes et autoriser les manettes à envoyer le modèle commandes.

Définir la mise en page personnalisée de la session

Pour indiquer aux applications clientes les commandes de lecture que vous souhaitez présenter à l'application définir la mise en page personnalisée de la session ; lorsque vous créez le MediaSession dans la méthode onCreate() de votre 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();
}

Déclarer le lecteur disponible et les commandes personnalisées

Les applications multimédias peuvent définir des commandes personnalisées utilisables, par exemple, une mise en page personnalisée. Par exemple, vous pouvez implémenter des boutons qui permettent au pour enregistrer un élément multimédia dans une liste d'éléments favoris. MediaController envoie des commandes personnalisées, et MediaSession.Callback les reçoit.

Vous pouvez définir les commandes de session personnalisées disponibles MediaController lorsqu'il se connecte à votre session multimédia. Pour y parvenir, remplacer MediaSession.Callback.onConnect(). Configurer et renvoyer l'ensemble des commandes disponibles lors de l'acceptation d'une demande de connexion MediaController dans la méthode de rappel 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();
  }
}

Pour recevoir des requêtes de commande personnalisées à partir d'un MediaController, remplacez le Méthode onCustomCommand() dans 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)
      );
    }
    ...
  }
}

Vous pouvez identifier le contrôleur multimédia qui envoie une requête à l'aide de la propriété Propriété packageName de l'objet MediaSession.ControllerInfo qui est transmises dans les méthodes Callback. Cela vous permet d'adapter en réponse à une commande donnée si celle-ci provient du système, votre propre application ou d'autres applications clientes.

Mettre à jour la mise en page personnalisée après une interaction utilisateur

Après avoir traité une commande personnalisée ou toute autre interaction avec le lecteur, vous vous devrez peut-être mettre à jour la disposition affichée dans l'UI de la manette. Exemple typique est un bouton d'activation qui modifie son icône après le déclenchement de l'action associée à l'aide de ce bouton. Pour mettre à jour la mise en page, vous pouvez utiliser 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));

Personnaliser le comportement des commandes de lecture

Pour personnaliser le comportement d'une commande définie dans l'interface Player, par exemple en tant que play() ou seekToNext(), encapsulez votre Player dans 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();

Pour en savoir plus sur ForwardingPlayer, consultez le guide ExoPlayer sur Personnalisation.

Identifier le contrôleur à l'origine de la demande d'une commande du joueur

Lorsqu'un appel à une méthode Player provient d'un MediaController, vous pouvez Identifier la source d'origine avec MediaSession.controllerForCurrentRequest et acquérez le ControllerInfo pour la requête actuelle:

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

Réagir aux boutons multimédias

Les boutons multimédias sont des boutons physiques présents sur les appareils Android et les autres périphériques tels que le bouton lecture/pause d'un casque Bluetooth. Poignées Media3 les événements du bouton multimédia à votre place lorsqu'ils arrivent à la session et appelle la méthode Player appropriée sur le lecteur de session.

Une application peut ignorer le comportement par défaut en remplaçant MediaSession.Callback.onMediaButtonEvent(Intent) Dans ce cas, l’application peut/doit gérer toutes les spécificités de l'API par elle-même.

Traitement des erreurs et création de rapports

Une session émet deux types d'erreurs et les signale aux contrôleurs. Les erreurs fatales signalent un échec technique de lecture de la session qui interrompt la lecture. Les erreurs fatales sont signalées au contrôleur automatiquement quand ils se produisent. Les erreurs non fatales ne sont pas d'ordre technique ou ne concernent pas le règlement erreurs qui n'interrompent pas la lecture et sont envoyées aux manettes par manuellement.

Erreurs de lecture fatales

Une erreur de lecture fatale est signalée à la session par le lecteur, puis signalées aux manettes pour qu'elles appellent Player.Listener.onPlayerError(PlaybackException) et Player.Listener.onPlayerErrorChanged(@Nullable PlaybackException)

Dans ce cas, l'état de lecture passe à STATE_IDLE. MediaController.getPlaybackError() renvoie l'PlaybackException qui a causé la transition. Un contrôleur peut inspecter le PlayerException.errorCode pour obtenir des informations sur le motif de l'erreur.

Pour des raisons d'interopérabilité, une erreur fatale est répliquée sur PlaybackStateCompat. de la session de la plate-forme en faisant passer son état à STATE_ERROR et en définissant code d'erreur et message en fonction de PlaybackException.

Personnalisation d'une erreur fatale

Pour fournir des informations localisées et significatives à l'utilisateur, les messages d'erreur et les extras d'une erreur de lecture fatale peuvent être personnalisés Utiliser un ForwardingPlayer lors de la création de la session:

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

Le lecteur de transfert enregistre un Player.Listener pour le joueur réel. et intercepte les rappels qui signalent une erreur. Une configuration personnalisée PlaybackException est ensuite délégué aux écouteurs sont enregistrés dans le lecteur de transfert. Pour que cela fonctionne, le lecteur de transfert remplace Player.addListener et Player.removeListener pour avoir accès Écouteurs avec lesquels envoyer un code d'erreur, un message ou des éléments supplémentaires personnalisés:

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

Erreurs non fatales

Les erreurs non fatales qui ne proviennent pas d'une exception technique peuvent être envoyées par une application à tous les contrôleurs ou à un contrôleur spécifique:

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

Une erreur non fatale envoyée au contrôleur de notification multimédia est répliquée sur PlaybackStateCompat de la session sur la plate-forme. Ainsi, seuls le code d'erreur et le message d'erreur est défini sur PlaybackStateCompat en conséquence, alors que PlaybackStateCompat.state n'est pas remplacé par STATE_ERROR.

Recevoir des erreurs non fatales

Un MediaController reçoit une erreur non fatale en implémentant 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.
              }
            });