监听播放事件
系统会将事件(例如状态更改和播放错误)报告给已注册的
Player.Listener
实例。要注册一个监听器来接收此类
事件:
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
的默认方法为空,因此您只需实现
您感兴趣的方法。有关完整说明,请参阅 Javadoc
方法以及调用时间。其中一些最重要的方法是
下文对此进行了更详细的介绍。
监听器可以选择实现单个事件回调或
在一个或多个事件发生后调用的通用 onEvents
回调
。请参阅 Individual callbacks vs onEvents
,了解
应针对不同的应用场景首选
播放状态更改
可以通过实现
onPlaybackStateChanged(@State int state)
Player.Listener
。播放器可能处于以下四种播放状态之一:
Player.STATE_IDLE
:这是初始状态,即播放器 以及停止播放的时间玩家只会拥有有限的资源 状态Player.STATE_BUFFERING
:播放器无法立即从其 当前位置。这主要是因为需要加载更多数据。Player.STATE_READY
:播放器能够立即从当前播放的内容开始播放 排名。Player.STATE_ENDED
:播放器已播放完所有媒体。
除了这些状态外,播放器还有一个 playWhenReady
标记
用户想要玩的游戏可通过实施
onPlayWhenReadyChanged(playWhenReady, @PlayWhenReadyChangeReason int reason)
。
播放器正在播放(即,播放器的位置正在播放,而媒体正在播放 则向用户展示)广告:
- 播放器处于
Player.STATE_READY
状态 playWhenReady
为true
- 播放因以下原因未停止播放:
Player.getPlaybackSuppressionReason
您不必单独检查这些属性,只需 Player.isPlaying
调用。可通过实现
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. } } });
播放错误
导致播放失败的错误可以通过
onPlayerError(PlaybackException error)
Player.Listener
。发生故障时,将调用此方法
播放状态转换为 Player.STATE_IDLE
之前立即触发。
失败或停止的播放可以调用 ExoPlayer.prepare
进行重试。
请注意,某些 Player
实现会传递
PlaybackException
提供有关失败的更多信息。对于
例如,ExoPlayer
会传递 ExoPlaybackException
,后者具有 type
,
rendererIndex
和其他 ExoPlayer 专用字段。
以下示例展示了如何检测因 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. } } } });
播放列表转换
每当播放器切换到播放列表中的新媒体项时
在注册 API 时调用 onMediaItemTransition(MediaItem mediaItem,
@MediaItemTransitionReason int reason)
Player.Listener
对象。“原因”表示相应请求是否为自动处理
过渡、跳转(例如在调用 player.next()
之后)、重复
同一项内容,或由播放列表更改所导致(例如,当前
播放项被移除)。
元数据
从 player.getCurrentMediaMetadata()
返回的元数据可能会由于多种原因
原因:播放列表转换、插播视频元数据更新或更新
播放过程中当前MediaItem
。
如果您对元数据更改感兴趣,例如为了更新显示
当前标题,您可以收听onMediaMetadataChanged
。
正在指定播放时间点
调用 Player.seekTo
方法会导致一系列对已注册的回调
Player.Listener
个实例:
reason=DISCONTINUITY_REASON_SEEK
会员价为onPositionDiscontinuity
。这是 调用Player.seekTo
的直接结果。该回调包含PositionInfo
字段。onPlaybackStateChanged
,其中包含与跳转相关的任何即时状态变化。 请注意,可能不会进行此类更改。
单个回调与 onEvents
监听器可以选择实现各个回调,例如
onIsPlayingChanged(boolean isPlaying)
,以及
onEvents(Player player, Events events)
回调。通用回调提供
访问 Player
对象,并指定发生的一组 events
。此回调函数始终在与
各个事件
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); } }
在以下情况下,应首选单个事件:
- 监听器对更改的原因感兴趣。例如,
针对
onPlayWhenReadyChanged
或onMediaItemTransition
提供的原因。 - 监听器仅对通过回调参数提供的新值执行操作 或触发其他不依赖于回调参数的操作。
- 监听器实现倾向于使用清晰易读的 触发了该事件。
- 监听器向需要了解所有事件数据的分析系统报告 具体事件和状态变化。
在onEvents(Player player, Events events)
以下情况:
- 监听器希望针对多个事件触发相同的逻辑。对于
示例:更新
onPlaybackStateChanged
和onPlayWhenReadyChanged
。 - 监听器需要访问
Player
对象才能触发更多事件, 例如,在媒体项过渡后跳转。 - 监听器打算使用
通过单独的回调一起进行,或与
Player
getter 结合使用 方法。例如,将Player.getCurrentWindowIndex()
与onTimelineChanged
中提供的Timeline
仅在onEvents
回调。 - 监听器关注的是事件在逻辑上是否一起发生。对于
例如,由于媒体项的影响,从
onPlaybackStateChanged
转移到STATE_BUFFERING
过渡效果。
在某些情况下,监听器可能需要将各个回调与
通用 onEvents
回调,例如用于记录媒体内容更改原因
与 onMediaItemTransition
搭配使用,但仅在可以使用所有状态变化后执行操作
并放入 onEvents
中。
使用 AnalyticsListener
使用 ExoPlayer
时,可以向播放器注册 AnalyticsListener
方法是调用 addAnalyticsListener
。AnalyticsListener
实现能够
监听可能有助于分析和日志记录的详细事件
目的。如需了解详情,请参阅数据分析页面。
使用 EventLogger
EventLogger
是库直接为实现以下目的而提供的 AnalyticsListener
:
记录目的。将 EventLogger
添加到 ExoPlayer
以启用实用功能
添加一行额外的日志记录:
Kotlin
player.addAnalyticsListener(EventLogger())
Java
player.addAnalyticsListener(new EventLogger());
如需了解详情,请参阅调试日志记录页面。
在指定播放位置触发事件
某些用例要求在指定播放位置触发事件。这是
使用 PlayerMessage
进行支持。您可以使用以下代码创建 PlayerMessage
:
ExoPlayer.createMessage
。应执行播放的播放位置
可使用 PlayerMessage.setPosition
进行设置。消息会在
播放线程,但也可以使用
PlayerMessage.setLooper
。可以使用PlayerMessage.setDeleteAfterDelivery
来控制每当指定的
播放位置(由于搜寻播放进度,可能会多次发生
和重复模式),也可以只发送一次。PlayerMessage
则可以使用 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();