Zdarzenia dotyczące odtwarzacza

Odsłuchiwanie zdarzeń odtwarzania

Zdarzenia, takie jak zmiany stanu czy błędy odtwarzania, są zgłaszane do zarejestrowanych instancji Player.Listener. Aby zarejestrować detektor odbierający 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 tylko wdrożyć metody, które Cię interesują. Pełny opis metod i czas ich wywoływania znajdziesz w Javadoc. Niektóre najważniejsze metody zostały szczegółowo omówione poniżej.

Detektory mogą wybrać między implementacją wywołań zwrotnych poszczególnych zdarzeń a ogólnym wywołaniem zwrotnym onEvents, które jest wywoływane po wystąpieniu co najmniej jednego zdarzenia razem. W sekcji Individual callbacks vs onEvents znajdziesz wyjaśnienia, które z nich powinny być preferowane w różnych przypadkach użycia.

Zmiany stanu odtwarzania

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

  • Player.STATE_IDLE: jest to stan początkowy, stan, w którym odtwarzacz został zatrzymany, i moment, w którym odtwarzanie nie powiodło się. W tym stanie gracz ma ograniczone zasoby.
  • Player.STATE_BUFFERING: odtwarzacz nie może od razu rozpocząć odtwarzania od bieżącej pozycji. Dzieje się tak głównie dlatego, że musi zostać wczytanych więcej danych.
  • Player.STATE_READY: odtwarzacz może od razu rozpocząć grę 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, że użytkownik chce zagrać. Zmiany w tej fladze można odbierać, implementując onPlayWhenReadyChanged(playWhenReady, @PlayWhenReadyChangeReason int reason).

Odtwarzacz jest w trakcie odtwarzania (czyli jego pozycja przewija się, a użytkownik prezentuje multimedia), gdy są spełnione wszystkie 3 z tych warunków:

  • Odtwarzacz jest w stanie Player.STATE_READY
  • Wydarzenie playWhenReady zacznie się true
  • Odtwarzanie nie jest wstrzymane z powodu zwracanego przez funkcję Player.getPlaybackSuppressionReason

Zamiast sprawdzać poszczególne właściwości, możesz wywołać Player.isPlaying. Zmiany tego stanu można uzyskać, 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ą błąd odtwarzania, można napotkać, implementując onPlayerError(PlaybackException error) w zarejestrowanej konfiguracji Player.Listener. W przypadku niepowodzenia ta metoda jest wywoływana bezpośrednio przed zmianą stanu odtwarzania na Player.STATE_IDLE. Nieudane lub zatrzymane odtwarzanie można ponowić, wywołując ExoPlayer.prepare.

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

Z przykładu poniżej dowiesz się, jak wykryć, że 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 z playlist

Za każdym razem, gdy odtwarzacz zmieni się na nowy element multimedialny na playliście, metoda onMediaItemTransition(MediaItem mediaItem, @MediaItemTransitionReason int reason) jest wywoływana dla zarejestrowanych obiektów Player.Listener. Przyczyna wskazuje, czy było to automatyczne przeniesienie, przewinięcie (np. po wywołaniu funkcji player.next()), powtórzenie tego samego elementu czy zmiana playlisty (np. usunięcie aktualnie odtwarzanego elementu).

Metadane

Metadane zwracane przez funkcję player.getCurrentMediaMetadata() mogą się zmienić z wielu powodów, takich jak przejścia playlisty, aktualizacje metadanych In-Stream lub aktualizacja bieżącej operacji MediaItem w trakcie odtwarzania.

Jeśli chcesz wprowadzić zmiany w metadanych, na przykład aby zaktualizować interfejs wyświetlający bieżący tytuł, możesz posłuchać: onMediaMetadataChanged.

Szukam

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

  1. onPositionDiscontinuity i reason=DISCONTINUITY_REASON_SEEK. Jest to bezpośredni wynik wywołania Player.seekTo. Wywołanie zwrotne zawiera pola (PositionInfo) dla pozycji przed wyszukiwaniem i po nim.
  2. onPlaybackStateChanged z dowolną natychmiastową zmianą stanu związaną z wyszukiwaniem. Pamiętaj, że taka zmiana może nie wystąpić.

Indywidualne wywołania zwrotne a onEvents

Słuchacze mogą wybrać między implementacją poszczególnych wywołań zwrotnych, np. 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 zbiór events, które wystąpiły razem. Jest ono zawsze wywoływane po wywołaniach 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);
  }
}

Pojedyncze wydarzenia powinny być preferowane w tych przypadkach:

  • Czytelnik jest zainteresowany przyczynami zmian. na przykład przyczyny podane w przypadku onPlayWhenReadyChanged lub onMediaItemTransition.
  • Detektor obsługuje tylko nowe wartości podane w parametrach wywołania zwrotnego lub wywołuje coś, co nie jest zależne od parametrów wywołania zwrotnego.
  • Implementacja na potrzeby odbiornika preferuje użycie w nazwie metody wyraźnego wskazania tego, co spowodowało zdarzenie.
  • Detektor przekazuje dane do systemu analitycznego, który musi mieć dostęp do informacji o wszystkich zdarzeniach i zmianach stanu.

Typowy onEvents(Player player, Events events) powinien być preferowany w tych przypadkach:

  • Detektor chce aktywować tę samą logikę dla wielu zdarzeń. Może to być na przykład aktualizacja interfejsu onPlaybackStateChanged i onPlayWhenReadyChanged.
  • Detektor potrzebuje dostępu do obiektu Player, aby aktywować kolejne zdarzenia, np. wyszukiwanie po przeniesieniu elementu multimedialnego.
  • Detektor zamierza używać wielu wartości stanu raportowanych za pomocą osobnych wywołań zwrotnych lub w połączeniu z metodami pobierającymi Player. Na przykład użycie Player.getCurrentWindowIndex() z Timeline podanym w onTimelineChanged jest bezpieczne tylko w wywołaniu zwrotnym onEvents.
  • Detektor jest zainteresowany tym, czy zdarzenia logicznie wystąpiły razem. Przykład: z onPlaybackStateChanged na STATE_BUFFERING z powodu przeniesienia elementu multimedialnego.

W niektórych przypadkach odbiorniki mogą być zmuszone do połączenia poszczególnych wywołań zwrotnych z ogólnym wywołaniem zwrotnym onEvents, np. aby zarejestrować przyczyny zmiany elementu multimedialnego za pomocą funkcji onMediaItemTransition, ale te działania muszą wykonywać tylko wtedy, gdy wszystkie zmiany stanu można wykorzystać razem w onEvents.

Jak korzystać z aplikacji AnalyticsListener

Jeśli używasz ExoPlayer, można zarejestrować AnalyticsListener w odtwarzaczu, wywołując metodę addAnalyticsListener. Implementacje AnalyticsListener mogą wychwytywać szczegółowe zdarzenia, które mogą być przydatne do celów analitycznych i logowania. Więcej informacji znajdziesz na stronie ze statystykami.

Jak korzystać z aplikacji EventLogger

EventLogger to obiekt AnalyticsListener udostępniany bezpośrednio przez bibliotekę na potrzeby logowania. Dodaj EventLogger do ExoPlayer, aby umożliwić dodatkowe przydatne logowanie w jednym wierszu:

Kotlin

player.addAnalyticsListener(EventLogger())

Java

player.addAnalyticsListener(new EventLogger());

Więcej informacji znajdziesz na stronie rejestrowania debugowania.

Uruchamianie zdarzeń na określonych pozycjach odtwarzania

Niektóre przypadki użycia wymagają uruchamiania zdarzeń na określonych pozycjach odtwarzania. Ta funkcja jest obsługiwana za pomocą PlayerMessage. PlayerMessage można utworzyć za pomocą ExoPlayer.createMessage. Pozycję odtwarzania, w której ma być wykonywane, możesz ustawić za pomocą właściwości PlayerMessage.setPosition. Wiadomości są domyślnie wykonywane w wątku odtwarzania, ale można to dostosować za pomocą parametru PlayerMessage.setLooper. PlayerMessage.setDeleteAfterDelivery pozwala określić, czy wiadomość ma być wykonywana za każdym razem, gdy zostanie napotkana określona pozycja odtwarzania (może się to zdarzyć kilka razy ze względu na tryby przewijania i powtarzania), czy tylko za pierwszym razem. Po skonfigurowaniu zasobu PlayerMessage można go zaplanować 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();