播放器事件

监听播放事件

系统会将事件(例如状态更改和播放错误)报告给已注册的 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 状态
  • playWhenReadytrue
  • 播放因以下原因未停止播放: 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,后者具有 typerendererIndex 和其他 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 个实例:

  1. reason=DISCONTINUITY_REASON_SEEK会员价为 onPositionDiscontinuity。这是 调用 Player.seekTo 的直接结果。该回调包含 PositionInfo 字段。
  2. 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);
  }
}

在以下情况下,应首选单个事件:

  • 监听器对更改的原因感兴趣。例如, 针对 onPlayWhenReadyChangedonMediaItemTransition 提供的原因。
  • 监听器仅对通过回调参数提供的新值执行操作 或触发其他不依赖于回调参数的操作。
  • 监听器实现倾向于使用清晰易读的 触发了该事件。
  • 监听器向需要了解所有事件数据的分析系统报告 具体事件和状态变化。

onEvents(Player player, Events events) 以下情况:

  • 监听器希望针对多个事件触发相同的逻辑。对于 示例:更新 onPlaybackStateChangedonPlayWhenReadyChanged
  • 监听器需要访问 Player 对象才能触发更多事件, 例如,在媒体项过渡后跳转。
  • 监听器打算使用 通过单独的回调一起进行,或与 Player getter 结合使用 方法。例如,将 Player.getCurrentWindowIndex()onTimelineChanged 中提供的 Timeline 仅在 onEvents 回调。
  • 监听器关注的是事件在逻辑上是否一起发生。对于 例如,由于媒体项的影响,从 onPlaybackStateChanged 转移到 STATE_BUFFERING 过渡效果。

在某些情况下,监听器可能需要将各个回调与 通用 onEvents 回调,例如用于记录媒体内容更改原因 与 onMediaItemTransition 搭配使用,但仅在可以使用所有状态变化后执行操作 并放入 onEvents 中。

使用 AnalyticsListener

使用 ExoPlayer 时,可以向播放器注册 AnalyticsListener 方法是调用 addAnalyticsListenerAnalyticsListener实现能够 监听可能有助于分析和日志记录的详细事件 目的。如需了解详情,请参阅数据分析页面

使用 EventLogger

EventLogger 是库直接为实现以下目的而提供的 AnalyticsListener: 记录目的。将 EventLogger 添加到 ExoPlayer 以启用实用功能 添加一行额外的日志记录:

Kotlin

player.addAnalyticsListener(EventLogger())

Java

player.addAnalyticsListener(new EventLogger());

如需了解详情,请参阅调试日志记录页面

在指定播放位置触发事件

某些用例要求在指定播放位置触发事件。这是 使用 PlayerMessage 进行支持。您可以使用以下代码创建 PlayerMessageExoPlayer.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();