Como ouvir eventos de reprodução
Eventos, como mudanças de estado e erros de reprodução, são informados para instâncias
Player.Listener
registradas. Para registrar um listener e receber esses
eventos:
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
tem métodos padrão vazios, então você só precisa implementar
os métodos em que tem interesse. Consulte o Javadoc para uma descrição completa dos métodos e quando eles são chamados. Alguns dos métodos mais importantes estão
descritos com mais detalhes abaixo.
Os listeners têm a opção de implementar callbacks de eventos individuais ou
um callback onEvents
genérico que é chamado após a ocorrência de um ou mais eventos
juntos. Consulte Individual callbacks vs onEvents
para uma explicação sobre qual
é recomendado para diferentes casos de uso.
Mudanças no estado da reprodução
As mudanças no estado do player podem ser recebidas implementando
onPlaybackStateChanged(@State int state)
em um Player.Listener
registrado. O player pode estar em um dos quatro estados de reprodução:
Player.STATE_IDLE
: é o estado inicial, quando o player é interrompido e quando a reprodução falhou. O jogador terá apenas recursos limitados nesse estado.Player.STATE_BUFFERING
: o jogador não pode jogar imediatamente na posição atual. Isso acontece principalmente porque mais dados precisam ser carregados.Player.STATE_READY
: o player pode tocar imediatamente da posição atual.Player.STATE_ENDED
: o player terminou de reproduzir todas as mídias.
Além desses estados, o jogador tem uma flag playWhenReady
para indicar
a intenção do usuário de jogar. As mudanças nessa flag podem ser recebidas implementando
onPlayWhenReadyChanged(playWhenReady, @PlayWhenReadyChangeReason int reason)
.
Um player está sendo reproduzido, ou seja, a posição dele está avançando e a mídia está sendo apresentada ao usuário quando as três condições abaixo são atendidas:
- O player está no estado
Player.STATE_READY
playWhenReady
étrue
- A reprodução não é suprimida por um motivo retornado por
Player.getPlaybackSuppressionReason
.
Em vez de verificar essas propriedades individualmente, é possível chamar Player.isPlaying
. As mudanças nesse estado podem ser recebidas com a implementação de
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. } } });
Erros de reprodução
Erros que fazem com que a reprodução falhe podem ser recebidos implementando
onPlayerError(PlaybackException error)
em um Player.Listener
registrado. Quando ocorre uma falha, esse método é chamado
imediatamente antes da transição do estado de reprodução para Player.STATE_IDLE
.
Chame ExoPlayer.prepare
para tentar novamente as reproduções com falha ou interrompidas.
Algumas implementações de Player
transmitem instâncias de subclasses de
PlaybackException
para fornecer mais informações sobre a falha. Por
exemplo, ExoPlayer
transmite ExoPlaybackException
, que tem type
,
rendererIndex
e outros campos específicos do ExoPlayer.
O exemplo abaixo mostra como detectar quando uma reprodução falhou devido a um problema de rede 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. } } } });
Transições de playlists
Sempre que o player muda para um novo item de mídia na playlist,
onMediaItemTransition(MediaItem mediaItem,
@MediaItemTransitionReason int reason)
é chamado em objetos
Player.Listener
registrados. O motivo indica se foi uma transição
automática, uma busca (por exemplo, depois de chamar player.next()
), uma repetição do
mesmo item ou causada por uma mudança na playlist (por exemplo, se o item em reprodução foi removido).
Metadados
Os metadados retornados de player.getCurrentMediaMetadata()
podem mudar por vários
motivos: transições de playlist, atualizações de metadados in-stream ou atualização do
MediaItem
atual no meio da reprodução.
Se você tiver interesse em mudanças de metadados, por exemplo, para atualizar uma interface que mostre
o título atual, ouça onMediaMetadataChanged
.
Procurando
A chamada de métodos Player.seekTo
resulta em uma série de callbacks para instâncias
Player.Listener
registradas:
onPositionDiscontinuity
comreason=DISCONTINUITY_REASON_SEEK
. Esse é o resultado direto da chamada dePlayer.seekTo
. O callback tem camposPositionInfo
para a posição antes e depois da busca.onPlaybackStateChanged
com qualquer mudança de estado imediata relacionada à busca. Talvez não exista essa mudança.
Callbacks individuais x onEvents
Os listeners podem escolher entre implementar callbacks individuais, como
onIsPlayingChanged(boolean isPlaying)
, e o callback
onEvents(Player player, Events events)
genérico. O callback genérico fornece
acesso ao objeto Player
e especifica o conjunto de events
que ocorreram
juntos. Esse callback é sempre chamado depois dos callbacks que correspondem aos
eventos individuais.
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); } }
Dê preferência a eventos individuais nos seguintes casos:
- O listener está interessado nos motivos das mudanças. Por exemplo, os
motivos fornecidos para
onPlayWhenReadyChanged
ouonMediaItemTransition
. - O listener só age com base nos novos valores fornecidos pelos parâmetros de callback ou aciona algo que não dependa deles.
- A implementação do listener prefere uma indicação clara e legível do que acionou o evento no nome do método.
- O listener é informado para um sistema de análise que precisa saber sobre todos os eventos individuais e mudanças de estado.
O onEvents(Player player, Events events)
genérico precisa ter preferência nos
seguintes casos:
- O listener quer acionar a mesma lógica em vários eventos. Por
exemplo, atualizar uma interface para
onPlaybackStateChanged
eonPlayWhenReadyChanged
. - O listener precisa acessar o objeto
Player
para acionar outros eventos, por exemplo, buscar após uma transição de item de mídia. - O listener pretende usar diversos valores de estado que são informados
por callbacks separados ou em combinação com métodos getter
Player
. Por exemplo, o uso dePlayer.getCurrentWindowIndex()
com oTimeline
fornecido emonTimelineChanged
é seguro apenas dentro do callbackonEvents
. - O listener está interessado em saber se os eventos ocorreram logicamente juntos. Por
exemplo,
onPlaybackStateChanged
paraSTATE_BUFFERING
devido a uma transição de item de mídia.
Em alguns casos, os listeners podem precisar combinar os callbacks individuais com o
callback onEvents
genérico, por exemplo, para registrar motivos de mudança de item de mídia
com onMediaItemTransition
, mas agir apenas quando todas as mudanças de estado puderem ser usadas
em conjunto em onEvents
.
Como usar o AnalyticsListener
Ao usar ExoPlayer
, um AnalyticsListener
pode ser registrado com o player
chamando addAnalyticsListener
. As implementações de AnalyticsListener
podem
detectar eventos detalhados que podem ser úteis para fins de análise e
geração de registros. Consulte a página de análise para mais detalhes.
Como usar o EventLogger
EventLogger
é um AnalyticsListener
fornecido diretamente pela biblioteca para
fins de geração de registros. Adicione EventLogger
a um ExoPlayer
para ativar a geração de registros adicionais úteis com uma única linha:
Kotlin
player.addAnalyticsListener(EventLogger())
Java
player.addAnalyticsListener(new EventLogger());
Consulte a página de geração de registros de depuração para saber mais.
Como disparar eventos em posições de reprodução especificadas
Alguns casos de uso exigem o disparo de eventos em posições de reprodução especificadas. Isso é
compatível usando PlayerMessage
. Um PlayerMessage
pode ser criado usando
ExoPlayer.createMessage
. A posição da reprodução em que ele precisa ser executado
pode ser definida usando PlayerMessage.setPosition
. As mensagens são executadas na
linha de execução de reprodução por padrão, mas isso pode ser personalizado usando
PlayerMessage.setLooper
. PlayerMessage.setDeleteAfterDelivery
pode ser usado
para controlar se a mensagem será executada sempre que a posição de reprodução especificada
for encontrada (isso pode acontecer várias vezes devido aos modos
de busca e repetição) ou apenas na primeira vez. Depois que o PlayerMessage
for
configurado, ele poderá ser programado usando 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();