Sự kiện của người chơi

Nghe sự kiện phát

Các sự kiện, chẳng hạn như thay đổi về trạng thái và lỗi phát, được báo cáo cho các thực thể Player.Listener đã đăng ký. Cách đăng ký trình nghe để nhận các sự kiện như vậy:

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 có các phương thức mặc định trống, vì vậy, bạn chỉ cần triển khai các phương thức mà mình quan tâm. Hãy xem Javadoc để biết nội dung mô tả đầy đủ về các phương thức và thời điểm các phương thức này được gọi. Một số phương thức quan trọng nhất được mô tả chi tiết hơn dưới đây.

Trình nghe có lựa chọn giữa triển khai các lệnh gọi lại sự kiện riêng lẻ hoặc lệnh gọi lại onEvents chung được gọi sau khi một hoặc nhiều sự kiện xảy ra cùng nhau. Hãy xem Individual callbacks vs onEvents để biết nội dung giải thích về lựa chọn nào nên được ưu tiên cho các trường hợp sử dụng khác nhau.

Thay đổi trạng thái phát

Bạn có thể nhận được các thay đổi về trạng thái của người chơi bằng cách triển khai onPlaybackStateChanged(@State int state) trong Player.Listener đã đăng ký. Trình phát có thể ở một trong bốn trạng thái phát:

  • Player.STATE_IDLE: Đây là trạng thái ban đầu, trạng thái khi trình phát dừng và khi không phát được. Người chơi sẽ chỉ giữ tài nguyên hạn chế ở trạng thái này.
  • Player.STATE_BUFFERING: Người chơi không thể chơi ngay từ vị trí hiện tại. Điều này chủ yếu xảy ra vì bạn cần tải nhiều dữ liệu hơn.
  • Player.STATE_READY: Người chơi có thể chơi ngay lập tức từ vị trí hiện tại.
  • Player.STATE_ENDED: Trình phát đã phát xong tất cả nội dung nghe nhìn.

Ngoài các trạng thái này, người chơi còn có một cờ playWhenReady để cho biết ý định chơi của người dùng. Bạn có thể nhận được các thay đổi trong cờ này bằng cách triển khai onPlayWhenReadyChanged(playWhenReady, @PlayWhenReadyChangeReason int reason).

Người chơi đang phát (nghĩa là vị trí của người chơi đang thăng tiến và nội dung nghe nhìn đang hiển thị trước người dùng) khi đáp ứng cả 3 điều kiện sau:

  • Người chơi đang ở trạng thái Player.STATE_READY
  • playWhenReadytrue
  • Tính năng phát không bị chặn vì lý do Player.getPlaybackSuppressionReason trả về

Thay vì phải kiểm tra riêng từng thuộc tính, Player.isPlaying có thể được gọi. Bạn có thể nhận được các thay đổi đối với trạng thái này bằng cách triển khai 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.
        }
      }
    });

Lỗi phát lại

Bạn có thể nhận được các lỗi khiến không phát được bằng cách triển khai onPlayerError(PlaybackException error) trong Player.Listener đã đăng ký. Khi xảy ra lỗi, phương thức này sẽ được gọi ngay trước khi trạng thái phát chuyển sang Player.STATE_IDLE. Bạn có thể thử phát lại lượt phát không thành công hoặc bị dừng bằng cách gọi ExoPlayer.prepare.

Xin lưu ý rằng một số phương thức triển khai Player sẽ truyền các thực thể của lớp con của PlaybackException để cung cấp thêm thông tin về lỗi. Ví dụ: ExoPlayer truyền ExoPlaybackException, có type, rendererIndex và các trường dành riêng cho ExoPlayer khác.

Ví dụ sau cho biết cách phát hiện thời điểm phát không thành công do sự cố mạng 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.
          }
        }
      }
    });

Hiệu ứng chuyển đổi danh sách phát

Bất cứ khi nào trình phát thay đổi sang một mục nội dung nghe nhìn mới trong danh sách phát onMediaItemTransition(MediaItem mediaItem, @MediaItemTransitionReason int reason) sẽ được gọi trên các đối tượng Player.Listener đã đăng ký. Lý do cho biết đó là sự chuyển đổi tự động, một lượt tìm kiếm (ví dụ: sau khi gọi player.next()), sự lặp lại của cùng một mục hay do sự thay đổi trong danh sách phát (ví dụ: nếu mục đang phát bị xoá).

Metadata

Siêu dữ liệu được trả về từ player.getCurrentMediaMetadata() có thể thay đổi vì nhiều lý do: việc chuyển đổi danh sách phát, cập nhật siêu dữ liệu trong luồng phát hoặc cập nhật MediaItem hiện tại khi phát lại.

Nếu quan tâm đến các thay đổi về siêu dữ liệu, chẳng hạn như cập nhật giao diện người dùng cho thấy tiêu đề hiện tại, bạn có thể nghe onMediaMetadataChanged.

Đang tìm kiếm

Việc gọi các phương thức Player.seekTo dẫn đến một loạt lệnh gọi lại đến các thực thể Player.Listener đã đăng ký:

  1. onPositionDiscontinuity bằng reason=DISCONTINUITY_REASON_SEEK. Đây là kết quả trực tiếp của việc gọi Player.seekTo. Lệnh gọi lại có các trường PositionInfo dành cho vị trí trước và sau lệnh tua.
  2. onPlaybackStateChanged có bất kỳ thay đổi trạng thái tức thì nào liên quan đến lệnh tìm kiếm. Xin lưu ý rằng có thể không có sự thay đổi nào như vậy.

Lệnh gọi lại riêng lẻ so với onEvents

Người nghe có thể chọn giữa việc triển khai các lệnh gọi lại riêng lẻ như onIsPlayingChanged(boolean isPlaying) hoặc lệnh gọi lại onEvents(Player player, Events events) chung. Lệnh gọi lại chung cung cấp quyền truy cập vào đối tượng Player và chỉ định tập hợp events đã xảy ra cùng nhau. Lệnh gọi lại này luôn được gọi sau các lệnh gọi lại tương ứng với từng sự kiện riêng lẻ.

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);
  }
}

Bạn nên ưu tiên sử dụng các sự kiện riêng lẻ trong các trường hợp sau:

  • Trình nghe quan tâm đến lý do thay đổi. Ví dụ: lý do được đưa ra cho onPlayWhenReadyChanged hoặc onMediaItemTransition.
  • Trình nghe chỉ thao tác trên các giá trị mới được cung cấp thông qua tham số gọi lại hoặc kích hoạt một giá trị khác không phụ thuộc vào tham số gọi lại.
  • Phương thức triển khai trình nghe ưu tiên một chỉ báo rõ ràng (có thể đọc được) về điều gì đã kích hoạt sự kiện trong tên phương thức.
  • Trình nghe báo cáo cho một hệ thống phân tích cần biết về tất cả các sự kiện và thay đổi trạng thái riêng lẻ.

Bạn nên ưu tiên sử dụng onEvents(Player player, Events events) chung trong các trường hợp sau:

  • Trình nghe muốn kích hoạt cùng một logic cho nhiều sự kiện. Ví dụ: cập nhật giao diện người dùng cho cả onPlaybackStateChangedonPlayWhenReadyChanged.
  • Trình nghe cần truy cập vào đối tượng Player để kích hoạt các sự kiện khác, chẳng hạn như tìm kiếm sau khi chuyển đổi mục nội dung đa phương tiện.
  • Trình nghe có ý định sử dụng nhiều giá trị trạng thái được báo cáo thông qua các lệnh gọi lại riêng biệt cùng nhau hoặc kết hợp với các phương thức getter Player. Ví dụ: việc sử dụng Player.getCurrentWindowIndex() với Timeline được cung cấp trong onTimelineChanged chỉ an toàn trong lệnh gọi lại onEvents.
  • Người nghe quan tâm đến việc các sự kiện có xảy ra cùng nhau một cách hợp lý hay không. Ví dụ: onPlaybackStateChanged sang STATE_BUFFERING do chuyển đổi mục nội dung đa phương tiện.

Trong một số trường hợp, trình nghe có thể cần kết hợp các lệnh gọi lại riêng lẻ với lệnh gọi lại onEvents chung, chẳng hạn như để ghi lại lý do thay đổi mục nội dung đa phương tiện bằng onMediaItemTransition, nhưng chỉ thực hiện hành động sau khi có thể sử dụng tất cả các thay đổi về trạng thái cùng nhau trong onEvents.

Sử dụng AnalyticsListener

Khi sử dụng ExoPlayer, bạn có thể đăng ký AnalyticsListener với trình phát bằng cách gọi addAnalyticsListener. Quá trình triển khai AnalyticsListener có thể theo dõi các sự kiện chi tiết có thể hữu ích cho mục đích phân tích và ghi nhật ký. Vui lòng tham khảo trang số liệu phân tích để biết thêm thông tin chi tiết.

Sử dụng EventLogger

EventLogger là một AnalyticsListener do thư viện trực tiếp cung cấp cho mục đích ghi nhật ký. Thêm EventLogger vào ExoPlayer để bật tính năng ghi nhật ký bổ sung hữu ích bằng một dòng duy nhất:

Kotlin

player.addAnalyticsListener(EventLogger())

Java

player.addAnalyticsListener(new EventLogger());

Xem trang ghi nhật ký gỡ lỗi để biết thêm chi tiết.

Kích hoạt sự kiện tại các vị trí phát được chỉ định

Một số trường hợp sử dụng yêu cầu kích hoạt sự kiện tại vị trí phát được chỉ định. Bạn có thể sử dụng PlayerMessage để hỗ trợ thuộc tính này. Bạn có thể tạo PlayerMessage bằng ExoPlayer.createMessage. Bạn có thể đặt vị trí phát mà tại đó cần được thực thi bằng PlayerMessage.setPosition. Theo mặc định, các tin nhắn sẽ được thực thi trên chuỗi phát. Tuy nhiên, bạn có thể tuỳ chỉnh việc này bằng PlayerMessage.setLooper. Bạn có thể sử dụng PlayerMessage.setDeleteAfterDelivery để kiểm soát xem thông báo có được thực thi mỗi khi gặp vị trí phát đã chỉ định hay không (điều này có thể xảy ra nhiều lần do các chế độ tìm kiếm và lặp lại) hoặc chỉ ở lần đầu tiên. Sau khi định cấu hình PlayerMessage, bạn có thể lên lịch bằng 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();