Kiểm soát và quảng cáo tính năng phát bằng MediaSession

Phiên phát nội dung nghe nhìn là cách thức tương tác phổ biến với trình phát âm thanh hoặc trình phát video. Trong Media3, trình phát mặc định là lớp ExoPlayer, triển khai giao diện Player. Việc kết nối phiên phát nội dung nghe nhìn với trình phát cho phép ứng dụng quảng cáo việc phát nội dung nghe nhìn bên ngoài và nhận các lệnh phát từ các nguồn bên ngoài.

Các lệnh có thể bắt nguồn từ các nút vật lý, chẳng hạn như nút phát trên tai nghe hoặc điều khiển từ xa của TV. Các lệnh này cũng có thể đến từ các ứng dụng khách có trình điều khiển nội dung nghe nhìn, chẳng hạn như hướng dẫn "tạm dừng" cho Trợ lý Google. Phiên phát nội dung nghe nhìn sẽ uỷ quyền các lệnh này cho trình phát của ứng dụng đa phương tiện.

Thời điểm chọn phiên phát nội dung nghe nhìn

Khi triển khai MediaSession, bạn cho phép người dùng kiểm soát việc phát:

  • Thông qua tai nghe. Thường có các nút hoặc thao tác chạm mà người dùng có thể thực hiện trên tai nghe để phát hoặc tạm dừng nội dung nghe nhìn hoặc chuyển đến bản nhạc tiếp theo hoặc trước đó.
  • Bằng cách nói chuyện với Trợ lý Google. Một mẫu phổ biến là nói "Ok Google, tạm dừng" để tạm dừng mọi nội dung nghe nhìn đang phát trên thiết bị.
  • Thông qua đồng hồ Wear OS. Điều này giúp dễ dàng truy cập vào các nút điều khiển phát phổ biến nhất trong khi phát trên điện thoại.
  • Thông qua Các nút điều khiển nội dung nghe nhìn. Băng chuyền này hiển thị các nút điều khiển cho từng phiên phát nội dung nghe nhìn đang chạy.
  • Trên TV. Cho phép thực hiện các thao tác bằng các nút phát vật lý, nút điều khiển phát trên nền tảng và quản lý nguồn (ví dụ: nếu TV, loa thanh hoặc bộ thu A/V tắt hoặc đầu vào được chuyển, thì quá trình phát sẽ dừng trong ứng dụng).
  • Thông qua các nút điều khiển nội dung nghe nhìn Android Auto. Điều này cho phép điều khiển phát an toàn khi lái xe.
  • Và mọi quy trình bên ngoài khác cần ảnh hưởng đến việc phát.

Điều này rất hữu ích cho nhiều trường hợp sử dụng. Đặc biệt, bạn nên cân nhắc sử dụng MediaSession khi:

  • Bạn đang phát trực tuyến nội dung video dạng dài, chẳng hạn như phim hoặc truyền hình trực tuyến.
  • Bạn đang phát trực tuyến nội dung âm thanh dạng dài, chẳng hạn như podcast hoặc danh sách phát nhạc.
  • Bạn đang xây dựng một ứng dụng truyền hình.

Tuy nhiên, không phải trường hợp sử dụng nào cũng phù hợp với MediaSession. Bạn có thể chỉ muốn sử dụng Player trong các trường hợp sau:

  • Bạn đang hiển thị nội dung dạng ngắn, trong đó không cần điều khiển bên ngoài hoặc phát trong nền.
  • Không có một video đang hoạt động, chẳng hạn như người dùng đang cuộn qua một danh sách và nhiều video được hiển thị trên màn hình cùng một lúc.
  • Bạn đang phát video giới thiệu hoặc giải thích một lần, mà bạn mong người dùng sẽ chủ động xem mà không cần các nút điều khiển phát bên ngoài.
  • Nội dung của bạn nhạy cảm về quyền riêng tư và bạn không muốn các quy trình bên ngoài truy cập vào siêu dữ liệu nội dung nghe nhìn (ví dụ: chế độ ẩn danh trong trình duyệt).

Nếu trường hợp sử dụng của bạn không phù hợp với bất kỳ trường hợp nào nêu trên, hãy cân nhắc xem bạn có đồng ý để ứng dụng tiếp tục phát khi người dùng không chủ động tương tác với nội dung hay không. Nếu câu trả lời là có, thì có lẽ bạn muốn chọn MediaSession. Nếu câu trả lời là không, thì có lẽ bạn muốn sử dụng Player.

Tạo phiên phát nội dung nghe nhìn

Phiên phát nội dung nghe nhìn tồn tại cùng với trình phát mà phiên đó quản lý. Bạn có thể tạo một phiên phát nội dung nghe nhìn bằng đối tượng ContextPlayer. Bạn nên tạo và khởi động một phiên phát nội dung nghe nhìn khi cần, chẳng hạn như phương thức vòng đời onStart() hoặc onResume() của Activity hoặc Fragment, hoặc phương thức onCreate() của Service sở hữu phiên phát nội dung nghe nhìn và trình phát được liên kết với phiên đó.

Để tạo một phiên phát nội dung nghe nhìn, hãy khởi động Player và cung cấp phiên đó cho MediaSession.Builder như sau:

Kotlin

val player = ExoPlayer.Builder(context).build()
val mediaSession = MediaSession.Builder(context, player).build()

Java

ExoPlayer player = new ExoPlayer.Builder(context).build();
MediaSession mediaSession = new MediaSession.Builder(context, player).build();

Xử lý trạng thái tự động

Thư viện Media3 tự động cập nhật phiên phát nội dung nghe nhìn bằng trạng thái của trình phát. Do đó, bạn không cần xử lý việc ánh xạ từ trình phát sang phiên theo cách thủ công.

Điều này khác với phiên phát nội dung nghe nhìn trên nền tảng, nơi bạn cần tạo và duy trì PlaybackState một cách độc lập với chính trình phát, chẳng hạn như để cho biết mọi lỗi.

Mã phiên duy nhất

Theo mặc định, MediaSession.Builder tạo một phiên có chuỗi trống làm mã phiên. Điều này là đủ nếu một ứng dụng chỉ định tạo một thực thể phiên duy nhất, đây là trường hợp phổ biến nhất.

Nếu một ứng dụng muốn quản lý nhiều thực thể phiên cùng một lúc, thì ứng dụng đó phải đảm bảo rằng mã phiên của mỗi phiên là duy nhất. Bạn có thể đặt mã phiên khi tạo phiên bằng MediaSession.Builder.setId(String id).

Nếu bạn thấy một IllegalStateException làm hỏng ứng dụng của mình bằng thông báo lỗi IllegalStateException: Session ID must be unique. ID= thì có khả năng là một phiên đã được tạo ngoài dự kiến trước khi một thực thể được tạo trước đó có cùng mã được phát hành. Để tránh các phiên bị rò rỉ do lỗi lập trình, những trường hợp như vậy sẽ được phát hiện và thông báo bằng cách đưa ra một ngoại lệ.

Cấp quyền kiểm soát cho các ứng dụng khác

Phiên phát nội dung nghe nhìn là chìa khoá để kiểm soát việc phát. Phiên này cho phép bạn định tuyến các lệnh từ các nguồn bên ngoài đến trình phát thực hiện công việc phát nội dung nghe nhìn. Các nguồn này có thể là các nút vật lý, chẳng hạn như nút phát trên tai nghe hoặc điều khiển từ xa của TV, hoặc các lệnh gián tiếp, chẳng hạn như hướng dẫn "tạm dừng" cho Trợ lý Google. Tương tự, bạn có thể muốn cấp quyền truy cập vào hệ thống Android để tạo điều kiện cho các nút điều khiển thông báo và màn hình khoá, hoặc vào đồng hồ Wear OS để bạn có thể điều khiển việc phát từ mặt đồng hồ. Các ứng dụng bên ngoài có thể sử dụng trình điều khiển nội dung nghe nhìn để đưa ra các lệnh phát cho ứng dụng đa phương tiện của bạn. Các lệnh này được phiên phát nội dung nghe nhìn của bạn nhận, cuối cùng sẽ uỷ quyền các lệnh cho trình phát nội dung nghe nhìn.

Sơ đồ minh hoạ hoạt động tương tác giữa MediaSession và MediaController.
Hình 1: Trình điều khiển nội dung nghe nhìn tạo điều kiện chuyển các lệnh từ các nguồn bên ngoài đến phiên nội dung nghe nhìn.

Khi một bộ điều khiển sắp kết nối với phiên phát nội dung nghe nhìn của bạn, onConnect() phương thức sẽ được gọi. Bạn có thể sử dụng ControllerInfo để quyết định có chấp nhận hay từ chối yêu cầu hay không. Xem ví dụ về việc chấp nhận yêu cầu kết nối trong phần Khai báo các lệnh tuỳ chỉnh.

Sau khi kết nối, bộ điều khiển có thể gửi các lệnh phát đến phiên. Sau đó, phiên sẽ uỷ quyền các lệnh đó cho trình phát. Các lệnh phát và danh sách phát được xác định trong giao diện Player sẽ được phiên tự động xử lý.

Các phương thức gọi lại khác cho phép bạn xử lý, ví dụ: các yêu cầu về lệnh tuỳ chỉnhsửa đổi danh sách phát. Các lệnh gọi lại này cũng bao gồm một đối tượng ControllerInfo để bạn có thể sửa đổi cách phản hồi từng yêu cầu trên cơ sở mỗi bộ điều khiển.

Sửa đổi danh sách phát

Phiên phát nội dung nghe nhìn có thể trực tiếp sửa đổi danh sách phát của trình phát như được giải thích trong the hướng dẫn ExoPlayer về danh sách phát. Bộ điều khiển cũng có thể sửa đổi danh sách phát nếu COMMAND_SET_MEDIA_ITEM hoặc COMMAND_CHANGE_MEDIA_ITEMS có sẵn cho bộ điều khiển.

Khi thêm các mục mới vào danh sách phát, trình phát thường yêu cầu MediaItem các thực thể có URI được xác định để phát các mục đó. Theo mặc định, các mục mới thêm sẽ tự động được chuyển tiếp đến các phương thức của trình phát như player.addMediaItem nếu các mục đó có URI được xác định.

Nếu muốn tuỳ chỉnh các thực thể MediaItem được thêm vào trình phát, bạn có thể ghi đè onAddMediaItems(). Bạn cần thực hiện bước này khi muốn hỗ trợ các bộ điều khiển yêu cầu nội dung nghe nhìn mà không có URI được xác định. Thay vào đó, MediaItem thường có một hoặc nhiều trường sau được đặt để mô tả nội dung nghe nhìn được yêu cầu:

  • MediaItem.id: Mã nhận dạng chung xác định nội dung nghe nhìn.
  • MediaItem.RequestMetadata.mediaUri: URI yêu cầu có thể sử dụng giản đồ tuỳ chỉnh và không nhất thiết phải phát trực tiếp được bằng trình phát.
  • MediaItem.RequestMetadata.searchQuery: Truy vấn tìm kiếm bằng văn bản, ví dụ: từ Trợ lý Google.
  • MediaItem.MediaMetadata: Siêu dữ liệu có cấu trúc như "title" (tiêu đề) hoặc "artist" (nghệ sĩ).

Để có thêm các lựa chọn tuỳ chỉnh cho danh sách phát hoàn toàn mới, bạn có thể ghi đè onSetMediaItems() cho phép bạn xác định mục bắt đầu và vị trí trong danh sách phát. Ví dụ: bạn có thể mở rộng một mục được yêu cầu thành toàn bộ danh sách phát và hướng dẫn trình phát bắt đầu ở chỉ mục của mục được yêu cầu ban đầu. Một mẫu triển khai onSetMediaItems() có tính năng này có thể tìm thấy trong ứng dụng minh họa phiên.

Quản lý lựa chọn ưu tiên về nút nội dung nghe nhìn

Mỗi bộ điều khiển, ví dụ: Giao diện người dùng hệ thống, Android Auto hoặc Wear OS, có thể tự quyết định hiển thị nút nào cho người dùng. Để cho biết các nút điều khiển phát mà bạn muốn hiển thị cho người dùng, bạn có thể chỉ định lựa chọn ưu tiên về nút nội dung nghe nhìn trên MediaSession. Các lựa chọn ưu tiên này bao gồm một danh sách có thứ tự các thực thể CommandButton, mỗi thực thể xác định một lựa chọn ưu tiên cho một nút trong giao diện người dùng.

Xác định các nút lệnh

Các thực thể CommandButton được dùng để xác định lựa chọn ưu tiên về nút nội dung nghe nhìn. Mỗi nút xác định 3 khía cạnh của phần tử trên giao diện người dùng mong muốn:

  1. Biểu tượng, xác định hình thức trực quan. Bạn phải đặt biểu tượng thành một trong các hằng số được xác định trước khi tạo CommandButton.Builder. Xin lưu ý rằng đây không phải là Bitmap thực tế hoặc tài nguyên hình ảnh. Hằng số chung giúp bộ điều khiển chọn một tài nguyên phù hợp để có giao diện nhất quán trong giao diện người dùng của riêng chúng. Nếu không có hằng số biểu tượng được xác định trước nào phù hợp với trường hợp sử dụng của bạn, thì bạn có thể sử dụng setCustomIconResId.
  2. The Lệnh, xác định hành động được kích hoạt khi người dùng tương tác với nút. Bạn có thể sử dụng setPlayerCommand cho Player.Command hoặc setSessionCommand cho SessionCommand tuỳ chỉnh hoặc được xác định trước.
  3. The Vị trí, xác định vị trí đặt nút trong giao diện người dùng bộ điều khiển. Trường này là không bắt buộc và được đặt tự động dựa trên Biểu tượngLệnh. Ví dụ: trường này cho phép chỉ định rằng một nút phải được hiển thị trong vùng điều hướng "tiến" của giao diện người dùng thay vì vùng "tràn" mặc định.

Kotlin

val button =
  CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD_15)
    .setPlayerCommand(Player.COMMAND_SEEK_FORWARD)
    .setSlots(CommandButton.SLOT_FORWARD)
    .build()

Java

CommandButton button =
    new CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD_15)
        .setPlayerCommand(Player.COMMAND_SEEK_FORWARD)
        .setSlots(CommandButton.SLOT_FORWARD)
        .build();

Khi các lựa chọn ưu tiên về nút nội dung nghe nhìn được phân giải, thuật toán sau sẽ được áp dụng:

  1. Đối với mỗi CommandButton trong lựa chọn ưu tiên về nút nội dung nghe nhìn, hãy đặt nút vào vị trí đầu tiên có sẵn và được phép.
  2. Nếu bất kỳ vị trí nào ở giữa, tiến và lùi không được điền bằng một nút, hãy thêm các nút mặc định cho vị trí này.

Bạn có thể sử dụng CommandButton.DisplayConstraints để tạo bản xem trước về cách phân giải các lựa chọn ưu tiên về nút nội dung nghe nhìn tuỳ thuộc vào các ràng buộc hiển thị giao diện người dùng.

Đặt lựa chọn ưu tiên về nút nội dung nghe nhìn

Cách dễ nhất để đặt lựa chọn ưu tiên về nút nội dung nghe nhìn là xác định danh sách khi tạo MediaSession. Ngoài ra, bạn có thể ghi đè MediaSession.Callback.onConnect để tuỳ chỉnh lựa chọn ưu tiên về nút nội dung nghe nhìn cho từng bộ điều khiển được kết nối.

Kotlin

val mediaSession =
  MediaSession.Builder(context, player)
    .setMediaButtonPreferences(ImmutableList.of(likeButton, favoriteButton))
    .build()

Java

MediaSession mediaSession =
    new MediaSession.Builder(context, player)
        .setMediaButtonPreferences(ImmutableList.of(likeButton, favoriteButton))
        .build();

Cập nhật lựa chọn ưu tiên về nút nội dung nghe nhìn sau khi người dùng tương tác

Sau khi xử lý một lượt tương tác với trình phát, bạn có thể muốn cập nhật các nút hiển thị trong giao diện người dùng bộ điều khiển. Một ví dụ điển hình là nút bật/tắt thay đổi biểu tượng và hành động sau khi kích hoạt hành động được liên kết với nút này. Để cập nhật lựa chọn ưu tiên về nút nội dung nghe nhìn, bạn có thể sử dụng MediaSession.setMediaButtonPreferences để cập nhật lựa chọn ưu tiên cho tất cả bộ điều khiển hoặc một bộ điều khiển cụ thể:

Kotlin

// Handle "favoritesButton" action, replace by opposite button
mediaSession.setMediaButtonPreferences(ImmutableList.of(likeButton, removeFromFavoritesButton))

Java

// Handle "favoritesButton" action, replace by opposite button
mediaSession.setMediaButtonPreferences(ImmutableList.of(likeButton, removeFromFavoritesButton));

Thêm các lệnh tuỳ chỉnh và tuỳ chỉnh hành vi mặc định

Bạn có thể mở rộng các lệnh trình phát có sẵn bằng các lệnh tuỳ chỉnh và cũng có thể chặn các lệnh trình phát và nút nội dung nghe nhìn đến để thay đổi hành vi mặc định.

Khai báo và xử lý các lệnh tuỳ chỉnh

Các ứng dụng đa phương tiện có thể xác định các lệnh tuỳ chỉnh mà ví dụ: có thể được dùng trong lựa chọn ưu tiên về nút nội dung nghe nhìn. Ví dụ: bạn có thể muốn triển khai các nút cho phép người dùng lưu một mục nội dung nghe nhìn vào danh sách các mục yêu thích. MediaController gửi các lệnh tuỳ chỉnh và MediaSession.Callback nhận các lệnh đó.

Để xác định các lệnh tuỳ chỉnh, bạn cần ghi đè MediaSession.Callback.onConnect() để đặt các lệnh tuỳ chỉnh có sẵn cho từng bộ điều khiển được kết nối.

Kotlin

private class CustomMediaSessionCallback : MediaSession.Callback {

  // Configure commands available to the controller in onConnect()
  override fun onConnectAsync(
    session: MediaSession,
    controller: ControllerInfo,
  ): ListenableFuture<ConnectionResult> {
    val sessionCommands =
      ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon()
        .add(SessionCommand(SAVE_TO_FAVORITES, Bundle.EMPTY))
        .build()
    return Futures.immediateFuture(
      AcceptedResultBuilder(session).setAvailableSessionCommands(sessionCommands).build()
    )
  }
}

Java

private static class CustomMediaSessionCallback implements MediaSession.Callback {

  // Configure commands available to the controller in onConnect()
  @Override
  public ListenableFuture<ConnectionResult> onConnectAsync(
      MediaSession session, ControllerInfo controller) {
    SessionCommands sessionCommands =
        ConnectionResult.DEFAULT_SESSION_COMMANDS
            .buildUpon()
            .add(new SessionCommand(SAVE_TO_FAVORITES, new Bundle()))
            .build();
    return Futures.immediateFuture(
        new AcceptedResultBuilder(session).setAvailableSessionCommands(sessionCommands).build());
  }
}

Để nhận các yêu cầu lệnh tuỳ chỉnh từ MediaController, hãy ghi đè phương thức onCustomCommand() trong Callback.

Kotlin

private class CustomCallback : MediaSession.Callback {
  // ...
  override fun onCustomCommand(
    session: MediaSession,
    controller: ControllerInfo,
    customCommand: SessionCommand,
    args: Bundle,
  ): ListenableFuture<SessionResult> {
    if (customCommand.customAction == SAVE_TO_FAVORITES) {
      // Do custom logic here
      saveToFavorites(session.player.currentMediaItem)
      return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
    }
    // ...
    return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
  }
}

Java

private static class CustomCallback implements MediaSession.Callback {
  // ...
  @Override
  public ListenableFuture<SessionResult> onCustomCommand(
      MediaSession session,
      ControllerInfo controller,
      SessionCommand customCommand,
      Bundle args) {
    if (customCommand.customAction.equals(SAVE_TO_FAVORITES)) {
      // Do custom logic here
      saveToFavorites(session.getPlayer().getCurrentMediaItem());
      return Futures.immediateFuture(new SessionResult(SessionResult.RESULT_SUCCESS));
    }
    // ...
    return Futures.immediateFuture(new SessionResult(SessionResult.RESULT_SUCCESS));
  }
}

Bạn có thể theo dõi trình điều khiển nội dung nghe nhìn nào đang đưa ra yêu cầu bằng cách sử dụng thuộc tính packageName của đối tượng MediaSession.ControllerInfo được truyền vào các phương thức Callback. Điều này cho phép bạn điều chỉnh hành vi của ứng dụng để phản hồi một lệnh nhất định nếu lệnh đó bắt nguồn từ hệ thống, ứng dụng của riêng bạn hoặc các ứng dụng khách khác.

Tuỳ chỉnh các lệnh trình phát mặc định

Tất cả các lệnh mặc định và việc xử lý trạng thái đều được uỷ quyền cho Player trên MediaSession. Để tuỳ chỉnh hành vi của một lệnh được xác định trong giao diện Player, chẳng hạn như play() hoặc seekToNext(), hãy gói Player trong ForwardingSimpleBasePlayer trước khi truyền lệnh đó đến MediaSession:

Kotlin

val forwardingPlayer =
  object : ForwardingSimpleBasePlayer(player) {
    // Customizations
  }

val mediaSession = MediaSession.Builder(context, forwardingPlayer).build()

Java

ForwardingSimpleBasePlayer forwardingPlayer = new ForwardingSimpleBasePlayer(player) {
      // Customizations
    };

MediaSession mediaSession = new MediaSession.Builder(context, forwardingPlayer).build();

Để biết thêm thông tin về ForwardingSimpleBasePlayer, hãy xem hướng dẫn ExoPlayer về Tuỳ chỉnh.

Xác định bộ điều khiển yêu cầu của lệnh trình phát

Khi một lệnh gọi đến phương thức Player bắt nguồn từ MediaController, bạn có thể xác định nguồn gốc bằng MediaSession.controllerForCurrentRequest và lấy ControllerInfo cho yêu cầu hiện tại:

Kotlin

private class CallerAwarePlayer(player: Player) : ForwardingSimpleBasePlayer(player) {
  private lateinit var session: MediaSession

  override fun handleSeek(
    mediaItemIndex: Int,
    positionMs: Long,
    seekCommand: Int,
  ): ListenableFuture<*> {
    Log.d(
      "caller",
      "seek operation from package ${session.controllerForCurrentRequest?.packageName}",
    )
    return super.handleSeek(mediaItemIndex, positionMs, seekCommand)
  }
}

Java

private static final class CallerAwarePlayer extends ForwardingSimpleBasePlayer {
  private MediaSession session;

  public CallerAwarePlayer(Player player) {
    super(player);
  }

  @Override
  protected ListenableFuture<?> handleSeek(int mediaItemIndex, long positionMs, int seekCommand) {
    Log.d(
        "caller",
        "seek operation from package: "
            + session.getControllerForCurrentRequest().getPackageName());
    return super.handleSeek(mediaItemIndex, positionMs, seekCommand);
  }
}

Tuỳ chỉnh việc xử lý nút nội dung nghe nhìn

Nút nội dung nghe nhìn là các nút phần cứng có trên thiết bị Android và các thiết bị ngoại vi khác, chẳng hạn như nút phát/tạm dừng trên tai nghe Bluetooth. Media3 xử lý các sự kiện nút nội dung nghe nhìn cho bạn khi các sự kiện này đến phiên và gọi phương thức Player thích hợp trên trình phát phiên.

Bạn nên xử lý tất cả các sự kiện nút nội dung nghe nhìn đến trong phương thức Player tương ứng. Đối với các trường hợp sử dụng nâng cao hơn, các sự kiện nút nội dung nghe nhìn có thể bị chặn trong MediaSession.Callback.onMediaButtonEvent(Intent).

Xử lý và báo cáo lỗi

Có 2 loại lỗi mà phiên phát ra và báo cáo cho bộ điều khiển. Lỗi nghiêm trọng báo cáo lỗi phát kỹ thuật của trình phát phiên làm gián đoạn quá trình phát. Lỗi nghiêm trọng được báo cáo tự động cho bộ điều khiển khi xảy ra. Lỗi không nghiêm trọng là lỗi không phải lỗi kỹ thuật hoặc lỗi chính sách không làm gián đoạn quá trình phát và được ứng dụng gửi đến bộ điều khiển theo cách thủ công.

Lỗi phát nghiêm trọng

Lỗi phát nghiêm trọng được trình phát báo cáo cho phiên, sau đó được báo cáo cho bộ điều khiển để gọi thông qua Player.Listener.onPlayerError(PlaybackException)Player.Listener.onPlayerErrorChanged(@Nullable PlaybackException).

Trong trường hợp như vậy, trạng thái phát được chuyển sang STATE_IDLEMediaController.getPlaybackError() trả về PlaybackException gây ra quá trình chuyển đổi. Bộ điều khiển có thể kiểm tra PlayerException.errorCode để biết thông tin về lý do gây ra lỗi.

Đặt lỗi trình phát tuỳ chỉnh

Ngoài các lỗi nghiêm trọng do trình phát báo cáo, ứng dụng có thể đặt PlaybackException tuỳ chỉnh ở cấp MediaSession bằng cách sử dụng MediaSession.setPlaybackException(PlaybackException). Điều này cho phép ứng dụng báo hiệu trạng thái lỗi cho các bộ điều khiển được kết nối. Bạn có thể đặt ngoại lệ cho tất cả bộ điều khiển được kết nối hoặc cho một ControllerInfo cụ thể.

Khi một ứng dụng đặt PlaybackException bằng API này:

  • Các thực thể MediaController được kết nối sẽ nhận được thông báo. Các lệnh gọi lại Listener.onPlayerError(PlaybackException)Listener.onPlayerErrorChanged(@Nullable PlaybackException) trên bộ điều khiển sẽ được gọi bằng ngoại lệ được cung cấp.

  • Phương thức MediaController.getPlayerError() sẽ trả về PlaybackException do ứng dụng đặt.

  • Trạng thái phát cho các bộ điều khiển bị ảnh hưởng sẽ thay đổi thành Player.STATE_IDLE.

  • Các lệnh có sẵn sẽ bị xoá và chỉ còn lại các lệnh đọc như COMMAND_GET_TIMELINE trong trường hợp các lệnh này đã được cấp. Ví dụ: trạng thái của Timeline bị đóng băng thành trạng thái khi ngoại lệ được áp dụng cho bộ điều khiển. Các lệnh cố gắng thay đổi trạng thái của trình phát, chẳng hạn như COMMAND_PLAY, sẽ bị xoá cho đến khi ứng dụng xoá ngoại lệ phát cho bộ điều khiển đã cho.

Để xoá PlaybackException tuỳ chỉnh đã đặt trước đó và khôi phục tính năng báo cáo trạng thái trình phát bình thường, ứng dụng có thể gọi MediaSession.setPlaybackException(/* playbackException= */ null) hoặc MediaSession.setPlaybackException(ControllerInfo, /* playbackException= */ null).

Tuỳ chỉnh lỗi nghiêm trọng

Để cung cấp thông tin có ý nghĩa và được bản địa hoá cho người dùng, bạn có thể tuỳ chỉnh mã lỗi, thông báo lỗi và các thông tin bổ sung về lỗi của lỗi phát nghiêm trọng đến từ trình phát thực tế. Bạn có thể thực hiện việc này bằng cách sử dụng ForwardingPlayer khi tạo phiên:

Kotlin

val session = MediaSession.Builder(context, ErrorForwardingPlayer(context, player)).build()

Java

MediaSession session =
    new MediaSession.Builder(context, new ErrorForwardingPlayer(context, player)).build();

Trình phát chuyển tiếp có thể sử dụng ForwardingSimpleBasePlayer để chặn lỗi và tuỳ chỉnh mã lỗi, thông báo hoặc thông tin bổ sung. Tương tự, bạn cũng có thể tạo các lỗi mới không tồn tại trong trình phát ban đầu:

Kotlin

private class ErrorForwardingPlayer(private val context: Context, player: Player) :
  ForwardingSimpleBasePlayer(player) {

  override fun getState(): State {
    var state = super.getState()
    if (state.playerError != null) {
      state =
        state.buildUpon().setPlayerError(customizePlaybackException(state.playerError!!)).build()
    }
    return state
  }

  private fun customizePlaybackException(error: PlaybackException): PlaybackException {
    val buttonLabel: String
    val errorMessage: String
    when (error.errorCode) {
      PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW -> {
        buttonLabel = context.getString(R.string.err_button_label_restart_stream)
        errorMessage = context.getString(R.string.err_msg_behind_live_window)
      }
      else -> {
        buttonLabel = context.getString(R.string.err_button_label_ok)
        errorMessage = context.getString(R.string.err_message_default)
      }
    }
    val extras = Bundle()
    extras.putString("button_label", buttonLabel)
    return PlaybackException(errorMessage, error.cause, error.errorCode, extras)
  }
}

Java

private static class ErrorForwardingPlayer extends ForwardingSimpleBasePlayer {

  private final Context context;

  public ErrorForwardingPlayer(Context context, Player player) {
    super(player);
    this.context = context;
  }

  @Override
  protected State getState() {
    State state = super.getState();
    if (state.playerError != null) {
      state =
          state.buildUpon().setPlayerError(customizePlaybackException(state.playerError)).build();
    }
    return state;
  }

  private PlaybackException customizePlaybackException(PlaybackException error) {
    String buttonLabel;
    String errorMessage;
    switch (error.errorCode) {
      case PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW:
        buttonLabel = context.getString(R.string.err_button_label_restart_stream);
        errorMessage = context.getString(R.string.err_msg_behind_live_window);
        break;
      default:
        buttonLabel = context.getString(R.string.err_button_label_ok);
        errorMessage = context.getString(R.string.err_message_default);
        break;
    }
    Bundle extras = new Bundle();
    extras.putString("button_label", buttonLabel);
    return new PlaybackException(errorMessage, error.getCause(), error.errorCode, extras);
  }
}

Lỗi không nghiêm trọng

Ứng dụng có thể gửi các lỗi không nghiêm trọng không bắt nguồn từ ngoại lệ kỹ thuật đến tất cả hoặc đến một bộ điều khiển cụ thể:

Kotlin

val sessionError =
  SessionError(
    SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED,
    context.getString(R.string.error_message_authentication_expired),
  )

// Option 1: Sending a nonfatal error to all controllers.
mediaSession.sendError(sessionError)

// Option 2: Sending a nonfatal error to the media notification controller only
// to set the error code and error message in the playback state of the platform
// media session.
mediaSession.mediaNotificationControllerInfo?.let { mediaSession.sendError(it, sessionError) }

Java

SessionError sessionError =
    new SessionError(
        SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED,
        context.getString(R.string.error_message_authentication_expired));

// Option 1: Sending a nonfatal error to all controllers.
mediaSession.sendError(sessionError);

// Option 2: Sending a nonfatal error to the media notification controller only
// to set the error code and error message in the playback state of the platform
// media session.
ControllerInfo mediaNotificationControllerInfo =
    mediaSession.getMediaNotificationControllerInfo();
if (mediaNotificationControllerInfo != null) {
  mediaSession.sendError(mediaNotificationControllerInfo, sessionError);
}

Khi một lỗi không nghiêm trọng được gửi đến bộ điều khiển thông báo nội dung nghe nhìn, mã lỗi và thông báo lỗi sẽ được sao chép vào phiên phát nội dung nghe nhìn trên nền tảng, trong khi PlaybackState.state không thay đổi thành STATE_ERROR.

Nhận lỗi không nghiêm trọng

Một MediaController nhận lỗi không nghiêm trọng bằng cách triển khai MediaController.Listener.onError:

Kotlin

val future =
  MediaController.Builder(context, sessionToken)
    .setListener(
      object : MediaController.Listener {
        override fun onError(controller: MediaController, sessionError: SessionError) {
          // Handle nonfatal error.
        }
      }
    )
    .buildAsync()

Java

MediaController.Builder future =
    new MediaController.Builder(context, sessionToken)
        .setListener(
            new MediaController.Listener() {
              @Override
              public void onError(MediaController controller, SessionError sessionError) {
                // Handle nonfatal error.
              }
            });