MediaSession kullanarak oynatmayı kontrol etme ve reklam yayınlama

Medya oturumları, ses veya video ile etkileşim için evrensel bir yol sunar oynatıcıya gitmiş olur. Media3'te varsayılan oynatıcı,ExoPlayer Player arayüzü. Medya oturumunu oynatıcıya bağladığınızda uygulama harici olarak medya oynatmanın reklamını yapmak ve harici kaynaklar.

Komutlar, kontrol düğmesindeki oynat düğmesi gibi fiziksel düğmelerden mikrofonlu kulaklık veya TV uzaktan kumandasını kullanın. Bunlar, masaüstü veya mobil "pause" (duraklatma) komutu vermek gibi Google Asistan'a bağlamalısınız. Medya oturumu, bu komutlar için medya uygulamasının oynatıcısına yetki verir.

Medya oturumu ne zaman seçilmelidir?

MediaSession öğesini uyguladığınızda, kullanıcıların oynatmayı kontrol etmesine izin vermiş olursunuz:

  • Kulaklıklarını kullanarak. Çoğunlukla bir cihazda düğmeler veya dokunma etkileşimleri kullanıcı kulaklığından medya içeriği oynatabilir veya duraklatabilir ya da sonraki veya önceki parçayı seçin.
  • Google Asistan'la konuşarak. Genellikle, "Tamam" demek için " Google, duraklat" düğmesine dokunun.
  • Wear OS kol saati üzerinden. Bu sayede en değerli bilgilere daha kolay genel oynatma kontrollerini kullanabilir.
  • Medya kontrolleri aracılığıyla. Bu bantta, her birine ait kontroller gösterilir nasıl kullanacağınızı göstereceğim.
  • TV'de. Fiziksel oynatma düğmeleri ve platform oynatma ile işlemlere izin verir ve güç yönetimi (örneğin, TV, ses çubuğu veya A/V alıcı Kullanıcı anahtarı kapanırsa veya giriş değiştirildiğinde, uygulamada oynatma durmalıdır).
  • Oynatmayı etkilemesi gereken diğer harici işlemler.

Bu, birçok kullanım alanı için idealdir. Özellikle de şu durumlarda MediaSession kullanılarak:

  • Filmler veya canlı TV gibi uzun video içeriklerini canlı oynatıyorsanız.
  • Podcast veya müzik gibi uzun ses içeriklerini canlı oynatıyorsanız oynatma listeleri.
  • TV uygulaması oluşturuyorsunuz.

Ancak tüm kullanım alanları MediaSession ile uyumlu değildir. Birlikte çalıştığınız Aşağıdaki durumlarda yalnızca Player kullanın:

  • Kullanıcı etkileşimi ve etkileşiminin yüksek olduğu kısa içerikler gösteriyorsunuz. son derece önemlidir.
  • Etkin tek bir video olmaması (örneğin, kullanıcı bir listede gezinirken) ekranda aynı anda birden fazla video görüntüleniyor.
  • Oynatacağınız tek seferlik bir tanıtım veya açıklama videosunu izlemelerini beklersiniz.
  • İçeriğiniz gizlilik açısından hassas olup harici işlemlerin medya meta verilerine erişme (örneğin, bir tarayıcıda gizli mod)

Kullanım alanınız yukarıda listelenenlerin hiçbirine uymuyorsa Kullanıcı aktif olarak etkileşimde bulunmadığında uygulamanızın oynatılmaya devam etmesinde bir sorun yok pek de iyi olmadığını unutmayın. Yanıtınız evet ise büyük olasılıkla şu seçeneği MediaSession Yanıtınız hayırsa Player kullanmak isteyebilirsiniz. .

Medya oturumu oluşturma

Yönettiği oynatıcının yanında bir medya oturumu gerçekleşir. Kontrol panelinizdeki Context ve Player nesnesiyle medya oturumu. Proje boyunca gerektiğinde (ör. onStart() veya Activity veya Fragment ya da onCreate() için onResume() yaşam döngüsü yöntemi medya oturumunun ve ilişkili oynatıcısının sahibi olan Service yöntemi.

Medya oturumu oluşturmak için bir Player başlatın ve MediaSession.Builder bunu beğendi:

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

Otomatik durum işleme

Media3 kitaplığı, oyuncunun durumu. Bu nedenle, eşlemeyi oyuncu oturumuna geçiyorum.

Bu, eski yaklaşımdan farklı bir yaklaşıma odaklanmak için gerekli olan, oyuncunun kendisinden bağımsız olarak bir PlaybackStateCompat (örneğin, lütfen unutmayın.

Benzersiz oturum kimliği

Varsayılan olarak MediaSession.Builder, aşağıdaki şekilde boş bir dizeyle oturum oluşturur: oturum kimliği. Uygulama yalnızca tek bir oturum örneğidir.

Bir uygulama aynı anda birden fazla oturum örneğini yönetmek istiyorsa her oturumun oturum kimliğinin benzersiz olmasını sağlamalıdır. Oturum kimliği MediaSession.Builder.setId(String id) ile oturum oluştururken ayarlanır.

Hata ile uygulamanızı kilitleyen bir IllegalStateException görürseniz IllegalStateException: Session ID must be unique. ID= adlı kullanıcıya mesaj gönder, o zaman bir oturumun, kullanıcı verileri ve kimlik doğrulama bilgileri gibi, beklenmedik bir şekilde aynı kimliğe sahip örnek yayınlandı. Oturumların bir kullanıcı tarafından sızdırılmasını önlemek için hata ayıklaması varsa bu tür durumlar tespit edilir ve kabul edersiniz.

Diğer istemcilere denetim izni ver

Medya oturumu, oynatmayı kontrol etmenin en önemli unsurudur. En küçük çaplı bir oynatıcıya veri aktarma işlemini yapan harici kaynaklardan gelen komutlar medya Bu kaynaklar, videodaki oynatma düğmesi gibi fiziksel düğmeler mikrofonlu kulaklık veya TV uzaktan kumandası ya da "duraklat" talimatı gibi dolaylı komutlar Google Asistan'a bağlamalısınız. Benzer şekilde, Android'e erişim izni vermek de veya Wear OS cihazları için Bu şekilde, oynatmayı kadrandan kontrol edebilirsiniz. Harici müşteriler şunları yapabilir: medya uygulamanıza oynatma komutları göndermek için medya denetleyici kullanın. Bunlar: medya oturumunuz tarafından alınır ve son olarak komutlara medya oynatıcıya gidin.

MediaSession ve MediaController arasındaki etkileşimi gösteren bir diyagram.
Şekil 1: Medya denetleyicisi, komutlarını harici kaynaklardan medya oturumuna aktarmanızı sağlar.
ziyaret edin.

Bir kumanda medya oturumunuza bağlanmak üzereyken onConnect() yöntemi çağrılır. Sağlanan ControllerInfo karar vermek için kabul etme veya reddet talep ediyor. Bağlantı isteğinin kabul edilmesine ilişkin bir örnek için Beyan kullanılabilir komutlar bölümüne bakın.

Bağlantı kurulduktan sonra kumanda, oturuma oynatma komutları gönderebilir. İlgili içeriği oluşturmak için kullanılan ardından bu komutları oyuncuya aktarır. Oynatma ve oynatma listesi Player arayüzünde tanımlanan komutlar, Search Ads 360'ta otomatik olarak kabul edilir.

Diğer geri çağırma yöntemleri, örneğin özel oynatma komutları ve oynatma listesini değiştirme). Bu geri çağırma işlevleri benzer şekilde, değişiklik yapabilmeniz için bir ControllerInfo nesnesi içerir. denetleyici bazında her isteğe nasıl yanıt verdiğinizi gösterir.

Oynatma listesini değiştirme

Bir medya oturumu, oynatıcısının oynatma listesini "the" Oynatma listeleri için ExoPlayer rehberi. Kumandalar ayrıca, aşağıdakilerden biri geçerliyse oynatma listesini de değiştirebilirler COMMAND_SET_MEDIA_ITEM veya COMMAND_CHANGE_MEDIA_ITEMS kullanabilir.

Oynatma listesine yeni öğeler eklenirken oynatıcı için genellikle MediaItem gösterilmesi gerekir. bir tanımlı URI bunları oynanabilir hale getirin. Varsayılan olarak, yeni eklenen öğeler otomatik olarak yönlendirilir. oynatıcı yöntemlerine (ör. player.addMediaItem) tanımlar.

Oynatıcıya eklenen MediaItem örneğini özelleştirmek isterseniz şunları yapabilirsiniz: geçersiz kılma onAddMediaItems() Bu adım, medya isteğinde bulunan kumandaları desteklemek istediğinizde gereklidir tanımlanamaz. Bunun yerine MediaItem genellikle aşağıdaki alanlardan biri veya daha fazlası istenen medyayı açıklamak üzere ayarlanmış olmalıdır:

  • MediaItem.id: Medyayı tanımlayan genel bir kimliktir.
  • MediaItem.RequestMetadata.mediaUri: Özel olabilecek bir istek URI'si bir şema oluşturur ve oynatıcı tarafından doğrudan oynatılamaz.
  • MediaItem.RequestMetadata.searchQuery: Metin biçiminde bir arama sorgusu, örneğin Google Asistan'dan.
  • MediaItem.MediaMetadata: "Başlık" gibi yapılandırılmış meta veriler "sanatçı" gibi düşünebilirsiniz.

Tamamen yeni oynatma listelerine yönelik daha fazla özelleştirme seçeneği için şunları yapabilirsiniz: ek olarak geçersiz kıl onSetMediaItems() oynatma listesindeki başlangıç öğesini ve konumu tanımlamanızı sağlar. Örneğin, istenen tek bir öğeyi oynatma listesinin tamamını genişletip oynatıcısının, ilk olarak istenen öğenin dizininde başlamasını sağlar. CEVAP onSetMediaItems() örneğinin uygulanması oturum demo uygulamasında bulabilirsiniz.

Özel düzeni ve özel komutları yönetin

Aşağıdaki bölümlerde, özel düzenin nasıl tanıtılacağı açıklanmaktadır. komut düğmelerini kullanarak istemci uygulamalarına komut gönderebilir ve denetleyicileri özel komutlarının ikisine katlanır.

Oturumun özel düzenini tanımlayın

İstemci uygulamalarına, hangi oynatma kontrollerini görüntülemek istediğinizi belirtmek için oturumun özel düzenini ayarlayın. onCreate() yönteminde MediaSession oluşturuyorsanız geliştirmenizi sağlar.

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

Kullanılabilir oynatıcıyı ve özel komutları bildir

Medya uygulamaları, örneğin şurada kullanılabilecek özel komutlar tanımlayabilir: oluşturmayı deneyin. Örneğin, kullanıcısının bir medya öğesini favori öğeler listesine kaydetmesini sağlayın. MediaController özel komutlar gönderir ve MediaSession.Callback bunları alır.

Bir Medya oturumunuza bağlandığında MediaController. Bu hedefe ulaşmak için MediaSession.Callback.onConnect() geçersiz kılınıyor. Yapılandır ve geri dön bir onConnect geri çağırma yönteminde MediaController:

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

MediaController öğesinden özel komut istekleri almak için Callback içinde onCustomCommand() yöntemi.

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

Hangi medya denetleyicisinin istekte bulunduğunu izlemek için MediaSession.ControllerInfo nesnesinin packageName özelliği Callback yönteme geçirildi. Bu sayede, uygulamanızın özelliklerini belli bir komut sistemden geliyorsa, ona yanıt olarak veya başka istemci uygulamaları içerebilir.

Kullanıcı etkileşiminden sonra özel düzeni güncelleme

Özel bir komutu veya oynatıcınızla başka herhangi bir etkileşimi gerçekleştirdikten sonra, kumanda arayüzünde gösterilen düzeni güncellemek isteyebilir. Tipik bir örnek ilgili işlemi tetikledikten sonra simgesini değiştiren bir açma/kapatma düğmesidir bu düğmeyi kullanın. Düzeni güncellemek için 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));

Oynatma komutu davranışını özelleştirme

Player arayüzünde tanımlanan bir komutun davranışını özelleştirmek için: play() veya seekToNext() olarak Player öğesini ForwardingPlayer içine yerleştirin.

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

ForwardingPlayer hakkında daha fazla bilgi için şu adresteki ExoPlayer kılavuzuna bakın: Özelleştirme.

Oynatıcı komutunun istekte bulunan denetleyicisini tanımlayın

Player yöntemine yapılan bir çağrı MediaController kaynağı tarafından gerçekleştirildiğinde şunu yapabilirsiniz: kaynak kaynağını MediaSession.controllerForCurrentRequest ile tanımlayın ve mevcut istek için ControllerInfo öğesini edinin:

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

Medya düğmelerine yanıt verme

Medya düğmeleri, Android cihazlarda ve diğer çevre birimlerinde bulunan donanım düğmeleridir. cihazları (örneğin, Bluetooth mikrofonlu kulaklıktaki oynat/duraklat düğmesi) kullanın. Medya 3 herkese açık kullanıcı adları oturuma geldiğinde ve Google Etiket Yöneticisi'ni çağırarak oturum oynatıcısında uygun Player yöntemini kullanın.

Bir uygulama, MediaSession.Callback.onMediaButtonEvent(Intent) Böyle bir durumda, uygulama tüm API özelliklerini kendi başına ele alabilir/ihtiyaçları vardır.

Hata işleme ve raporlama

Bir oturumun yayınladığı ve denetleyicilere bildirdiği iki tür hata vardır. Önemli hatalar, oturumdaki bir teknik oynatma hatasını bildirir oynatıcıyı oluşturur. Önemli hatalar denetleyiciye bildirilir otomatik olarak devreye girer. Önemli olmayan hatalar teknik veya politika ile ilgili olmayan hatalardır oluşturmayan ve denetleyicilere manuel olarak yapabilirsiniz.

Önemli oynatma hataları

Oynatıcı tarafından oturuma önemli bir oynatma hatası bildirilir ve ardından denetleyicilere rapor üzerinden iletilecek Player.Listener.onPlayerError(PlaybackException) ve Player.Listener.onPlayerErrorChanged(@Nullable PlaybackException).

Böyle bir durumda, oynatma durumu STATE_IDLE değerine geçirilir ve MediaController.getPlaybackError(), şunu döndürür: PlaybackException bahsedeceğim. Kumanda, şunları almak için PlayerException.errorCode cihazını inceleyebilir: hatanın nedeni hakkında bilgi edinin.

Birlikte çalışabilirlik için önemli bir hata PlaybackStateCompat cihazına çoğaltılır. durumunu STATE_ERROR olarak ayarlayıp hata kodu ve mesajı gösterilir.PlaybackException

Önemli bir hatanın özelleştirilmesi

Kullanıcıya yerelleştirilmiş ve anlamlı bilgiler sağlamak için hata kodu, önemli bir oynatma hatasına ilişkin hata mesajı ve hata ekstraları oturumu oluştururken ForwardingPlayer kullanarak:

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

Yönlendirme oynatıcı, gerçek oynatıcıya bir Player.Listener kaydeder ve hata bildiren geri çağırmalara müdahale eder. Özelleştirilmiş PlaybackException yetkisi verilen dinleyicilere verilir. yönlendirme oynatıcıda kayıtlıdır. Bunun işe yaraması için yönlendirme oynatıcısının Player.addListener ve Player.removeListener izinlerini geçersiz kılar özelleştirilmiş hata kodu, mesaj veya ekstraların gönderileceği işleyiciler:

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

Önemli olmayan hatalar

Teknik bir istisnadan kaynaklanmayan olmayan önemli olmayan hatalar gönderilebilir denetleyiciye uygulama aracılığıyla erişim sağlar:

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

Medya bildirim denetleyicisine gönderilen önemli olmayan bir hata, Platform oturumunun PlaybackStateCompat. Dolayısıyla, yalnızca hata kodu ve hata mesajı buna göre PlaybackStateCompat değerine ayarlanır. PlaybackStateCompat.state, STATE_ERROR olarak değiştirilmedi.

Önemli olmayan hataları al

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