Zdarzenia dotyczące odtwarzacza

Nasłuchiwanie zdarzeń odtwarzania

Zdarzenia, takie jak zmiany stanu i błędy odtwarzania, są zgłaszane do zarejestrowanych instancji Player.Listener. Aby zarejestrować detektor, który będzie odbierać takie zdarzenia:

Kotlin

// Add a listener to receive events from the player.
player.addListener(listener)

Java

// Add a listener to receive events from the player.
player.addListener(listener);

Player.Listener ma puste metody domyślne, więc musisz zaimplementować tylko te metody, które Cię interesują. Pełny opis metod i informacje o tym, kiedy są wywoływane, znajdziesz w dokumentacji Javadoc. Niektóre z najważniejszych metod opisujemy bardziej szczegółowo poniżej.

Słuchacze mogą wdrożyć wywołania zwrotne poszczególnych zdarzeń lub ogólne wywołanie zwrotne onEvents, które jest wywoływane po wystąpieniu co najmniej 1 zdarzenia naraz. Wyjaśnienie, które z nich są preferowane w różnych przypadkach użycia, znajdziesz w sekcji Individual callbacks vs onEvents.

Zmiany stanu odtwarzania

Zmiany stanu odtwarzacza można odbierać, implementując onPlaybackStateChanged(@State int state) w zarejestrowanym Player.Listener. Odtwarzacz może mieć jeden z 4 stanów odtwarzania:

  • Player.STATE_IDLE: Jest to stan początkowy, stan, w którym odtwarzacz jest zatrzymany i gdy odtwarzanie się nie powiodło. W tym stanie odtwarzacz będzie miał tylko ograniczone zasoby.
  • Player.STATE_BUFFERING: odtwarzacz nie może od razu odtworzyć treści od bieżącej pozycji. Dzieje się tak najczęściej, ponieważ trzeba wczytać więcej danych.
  • Player.STATE_READY: odtwarzacz może od razu odtwarzać od bieżącej pozycji.
  • Player.STATE_ENDED: odtwarzacz zakończył odtwarzanie wszystkich multimediów.

Oprócz tych stanów odtwarzacz ma flagę playWhenReady, która wskazuje zamiar użytkownika dotyczący odtwarzania. Zmiany tego stanu można otrzymywać, implementując onPlayWhenReadyChanged(playWhenReady, @PlayWhenReadyChangeReason int reason).

Odtwarzacz jest aktywny (tzn. jego pozycja się zmienia, a użytkownikowi są prezentowane multimedia), gdy spełnione są wszystkie 3 warunki:

  • Odtwarzacz jest w stanie Player.STATE_READY.
  • Wydarzenie playWhenReady zacznie się true
  • Odtwarzanie nie jest wstrzymywane z przyczyny zwróconej przez Player.getPlaybackSuppressionReason

Zamiast sprawdzać te właściwości pojedynczo, można wywołać funkcję Player.isPlaying. Zmiany tego stanu można odbierać, implementując onIsPlayingChanged(boolean isPlaying):

Kotlin

player.addListener(
  object : Player.Listener {
    override fun onIsPlayingChanged(isPlaying: Boolean) {
      if (isPlaying) {
        // Active playback.
      } else {
        // Not playing because playback is paused, ended, suppressed, or the player
        // is buffering, stopped or failed. Check player.playWhenReady,
        // player.playbackState, player.playbackSuppressionReason and
        // player.playerError for details.
      }
    }
  }
)

Java

player.addListener(
    new Player.Listener() {
      @Override
      public void onIsPlayingChanged(boolean isPlaying) {
        if (isPlaying) {
          // Active playback.
        } else {
          // Not playing because playback is paused, ended, suppressed, or the player
          // is buffering, stopped or failed. Check player.getPlayWhenReady,
          // player.getPlaybackState, player.getPlaybackSuppressionReason and
          // player.getPlaybackError for details.
        }
      }
    });

Błędy odtwarzania

Błędy, które powodują niepowodzenie odtwarzania, można odbierać, implementując onPlayerError(PlaybackException error) w zarejestrowanym Player.Listener. W przypadku wystąpienia błędu ta metoda zostanie wywołana natychmiast przed przejściem stanu odtwarzania do Player.STATE_IDLE. Nieudane lub zatrzymane odtwarzania można ponowić, wywołując funkcję ExoPlayer.prepare.

Pamiętaj, że niektóre implementacje Player przekazują instancje podklas PlaybackException, aby dostarczyć dodatkowe informacje o błędzie. Na przykład ExoPlayer przekazuje ExoPlaybackException, które zawiera type, rendererIndex i inne pola specyficzne dla ExoPlayera.

Poniższy przykład pokazuje, jak wykryć, kiedy odtwarzanie nie powiodło się z powodu problemu z siecią HTTP:

Kotlin

player.addListener(
  object : Player.Listener {
    override fun onPlayerError(error: PlaybackException) {
      val cause = error.cause
      if (cause is HttpDataSourceException) {
        // An HTTP error occurred.
        val httpError = cause
        // It's possible to find out more about the error both by casting and by querying
        // the cause.
        if (httpError is InvalidResponseCodeException) {
          // Cast to InvalidResponseCodeException and retrieve the response code, message
          // and headers.
        } else {
          // Try calling httpError.getCause() to retrieve the underlying cause, although
          // note that it may be null.
        }
      }
    }
  }
)

Java

player.addListener(
    new Player.Listener() {
      @Override
      public void onPlayerError(PlaybackException error) {
        @Nullable Throwable cause = error.getCause();
        if (cause instanceof HttpDataSourceException) {
          // An HTTP error occurred.
          HttpDataSourceException httpError = (HttpDataSourceException) cause;
          // It's possible to find out more about the error both by casting and by querying
          // the cause.
          if (httpError instanceof HttpDataSource.InvalidResponseCodeException) {
            // Cast to InvalidResponseCodeException and retrieve the response code, message
            // and headers.
          } else {
            // Try calling httpError.getCause() to retrieve the underlying cause, although
            // note that it may be null.
          }
        }
      }
    });

Przejścia między playlistami

Gdy odtwarzacz przełącza się na nowy element multimedialny na playliście, wywoływana jest funkcja onMediaItemTransition(MediaItem mediaItem, @MediaItemTransitionReason int reason) na zarejestrowanych obiektach Player.Listener. Przyczyna wskazuje, czy było to automatyczne przejście, przewijanie (np. po wywołaniu funkcji player.next()), powtórzenie tego samego elementu czy zmiana listy odtwarzania (np. jeśli aktualnie odtwarzany element zostanie usunięty).

Metadane

Metadane zwracane przez player.getCurrentMediaMetadata() mogą się zmieniać z wielu powodów: przejścia między playlistami, aktualizacje metadanych In-Stream lub aktualizacja bieżącego MediaItem w trakcie odtwarzania.

Jeśli interesują Cię zmiany metadanych, np. w celu zaktualizowania interfejsu, który wyświetla bieżący tytuł, możesz nasłuchiwać zdarzenia onMediaMetadataChanged.

Szukam

Wywołanie metod Player.seekTo powoduje serię wywołań zwrotnych do zarejestrowanych instancji Player.Listener:

  1. onPositionDiscontinuity z reason=DISCONTINUITY_REASON_SEEK. Jest to bezpośredni wynik wywołania funkcji Player.seekTo. Wywołanie zwrotne ma PositionInfo pola określające pozycję przed i po przewinięciu.
  2. onPlaybackStateChanged z natychmiastową zmianą stanu związaną z przewijaniem. Pamiętaj, że taka zmiana może nie nastąpić.

Pojedyncze oddzwanianie a onEvents

Słuchacze mogą wybrać między implementacją poszczególnych wywołań zwrotnych, takich jak onIsPlayingChanged(boolean isPlaying), a ogólnym wywołaniem zwrotnym onEvents(Player player, Events events). Ogólne wywołanie zwrotne zapewnia dostęp do obiektu Player i określa zestaw zdarzeń events, które wystąpiły razem. Ta funkcja zwrotna jest zawsze wywoływana po funkcjach zwrotnych odpowiadających poszczególnym zdarzeniom.

Kotlin

override fun onEvents(player: Player, events: Player.Events) {
  if (
    events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED) ||
      events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED)
  ) {
    uiModule.updateUi(player)
  }
}

Java

@Override
public void onEvents(Player player, Events events) {
  if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED)
      || events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED)) {
    uiModule.updateUi(player);
  }
}

W tych przypadkach zalecane są zdarzenia indywidualne:

  • Słuchacz jest zainteresowany przyczynami zmian. Na przykład powody podane w przypadku onPlayWhenReadyChanged lub onMediaItemTransition.
  • Odbiornik reaguje tylko na nowe wartości podane za pomocą parametrów wywołania zwrotnego lub wywołuje coś innego, co nie zależy od parametrów wywołania zwrotnego.
  • Implementacja odbiornika preferuje czytelną informację o tym, co wywołało zdarzenie, w nazwie metody.
  • Detektor raportuje do systemu analitycznego, który musi znać wszystkie poszczególne zdarzenia i zmiany stanu.

Ogólny tag onEvents(Player player, Events events) jest preferowany w tych przypadkach:

  • Słuchacz chce wywołać tę samą logikę w przypadku wielu zdarzeń. Na przykład aktualizowanie interfejsu zarówno dla onPlaybackStateChanged, jak i onPlayWhenReadyChanged.
  • Detektor musi mieć dostęp do obiektu Player, aby wywoływać kolejne zdarzenia, np. przewijanie po przejściu do innego elementu multimedialnego.
  • Detektor zamierza używać wielu wartości stanu zgłaszanych przez oddzielne wywołania zwrotne razem lub w połączeniu z Player metodami getter. Na przykład używanie parametru Player.getCurrentWindowIndex() z parametrem Timeline podanym w onTimelineChanged jest bezpieczne tylko w wywołaniu zwrotnym onEvents.
  • Detektor jest zainteresowany tym, czy zdarzenia wystąpiły logicznie razem. Na przykład z onPlaybackStateChanged na STATE_BUFFERING z powodu przejścia elementu multimedialnego.

W niektórych przypadkach słuchacze mogą potrzebować połączenia poszczególnych wywołań zwrotnych z ogólnym wywołaniem zwrotnym onEvents, np. aby rejestrować przyczyny zmiany elementu multimedialnego za pomocą onMediaItemTransition, ale działać dopiero wtedy, gdy wszystkie zmiany stanu mogą być używane razem w onEvents.

Jak korzystać z aplikacji AnalyticsListener

Podczas korzystania z ExoPlayer można zarejestrować AnalyticsListener w odtwarzaczu, dzwoniąc pod numer addAnalyticsListener. Implementacje AnalyticsListener mogą nasłuchiwać szczegółowych zdarzeń, które mogą być przydatne do celów analitycznych i rejestrowania. Więcej informacji znajdziesz na stronie z analityką.

Jak korzystać z aplikacji EventLogger

EventLogger to AnalyticsListener dostarczany bezpośrednio przez bibliotekę na potrzeby rejestrowania. Dodaj EventLogger do ExoPlayer, aby włączyć przydatne dodatkowe rejestrowanie za pomocą jednego wiersza:

Kotlin

player.addAnalyticsListener(EventLogger())

Java

player.addAnalyticsListener(new EventLogger());

Więcej informacji znajdziesz na stronie dotyczącej rejestrowania debugowania.

Wywoływanie zdarzeń w określonych momentach odtwarzania

Niektóre przypadki użycia wymagają uruchamiania zdarzeń w określonych pozycjach odtwarzania. Jest to obsługiwane za pomocą PlayerMessage. PlayerMessage można utworzyć za pomocąExoPlayer.createMessage. Pozycję odtwarzania, w której ma zostać wykonane działanie, można ustawić za pomocą parametru PlayerMessage.setPosition. Wiadomości są domyślnie wykonywane w wątku odtwarzania, ale można to dostosować za pomocą PlayerMessage.setLooper. PlayerMessage.setDeleteAfterDelivery może służyć do określania, czy wiadomość ma być wykonywana za każdym razem, gdy zostanie osiągnięta określona pozycja odtwarzania (może się to zdarzyć wielokrotnie ze względu na wyszukiwanie i tryby powtarzania), czy tylko za pierwszym razem. Po skonfigurowaniu PlayerMessage można zaplanować jego użycie za pomocą PlayerMessage.send.

Kotlin

player
  .createMessage { messageType: Int, payload: Any? -> }
  .setLooper(Looper.getMainLooper())
  .setPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 120000)
  .setPayload(customPayloadData)
  .setDeleteAfterDelivery(false)
  .send()

Java

player
    .createMessage(
        (messageType, payload) -> {
          // Do something at the specified playback position.
        })
    .setLooper(Looper.getMainLooper())
    .setPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 120_000)
    .setPayload(customPayloadData)
    .setDeleteAfterDelivery(false)
    .send();