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
playWhenReadyzacznie 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:
onPositionDiscontinuityzreason=DISCONTINUITY_REASON_SEEK. Jest to bezpośredni wynik wywołania funkcjiPlayer.seekTo. Wywołanie zwrotne maPositionInfopola określające pozycję przed i po przewinięciu.onPlaybackStateChangedz 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
onPlayWhenReadyChangedlubonMediaItemTransition. - 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 ionPlayWhenReadyChanged. - 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
Playermetodami getter. Na przykład używanie parametruPlayer.getCurrentWindowIndex()z parametremTimelinepodanym wonTimelineChangedjest bezpieczne tylko w wywołaniu zwrotnymonEvents. - Detektor jest zainteresowany tym, czy zdarzenia wystąpiły logicznie razem.
Na przykład z
onPlaybackStateChangednaSTATE_BUFFERINGz 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();