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:
onPositionDiscontinuity
ireason=DISCONTINUITY_REASON_SEEK
. Jest to bezpośredni wynik wywołaniaPlayer.seekTo
. Wywołanie zwrotne zawiera pola (PositionInfo
) dla pozycji przed wyszukiwaniem i po nim.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
lubonMediaItemTransition
. - 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
ionPlayWhenReadyChanged
. - 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życiePlayer.getCurrentWindowIndex()
zTimeline
podanym wonTimelineChanged
jest bezpieczne tylko w wywołaniu zwrotnymonEvents
. - Detektor jest zainteresowany tym, czy zdarzenia logicznie wystąpiły razem. Przykład: z
onPlaybackStateChanged
naSTATE_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();