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, lớp này triển khai giao diện Player. Việc kết nối phiên phát nội dung đa phương tiện với trình phát cho phép ứng dụng quảng cáo việc phát nội dung đa phương tiện bên ngoài và nhận lệnh phát từ các nguồn bên ngoài.

Lệnh có thể bắt nguồn từ các nút vật lý 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ư lệnh "tạm dừng" cho Trợ lý Google. Phiên nội dung nghe nhìn 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 đa phương tiện

Khi triển khai MediaSession, bạn cho phép người dùng kiểm soát chế độ phát:

  • Qua tai nghe. Thường thì người dùng có thể thực hiện các thao tác nhấn nút hoặc chạm trên tai nghe để phát hoặc tạm dừng nội dung nghe nhìn hoặc chuyển sang 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 hiện đang phát trên thiết bị.
  • Thông qua đồng hồ Wear OS. Điều này giúp người dùng dễ dàng truy cập vào các chế độ điều khiển phát phổ biến nhất 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 các thao tác bằng nút phát thực, đ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 bị chuyển đổi, thì quá trình phát sẽ dừng trong ứng dụng).
  • Và mọi quy trình bên ngoài khác cần ảnh hưởng đến quá trình phát.

Điều này rất phù hợp với nhiều trường hợp sử dụng. Cụ thể, bạn nên cân nhắc sử dụng MediaSession khi:

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

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, không cần có chế độ điều khiển bên ngoài hoặc phát ở chế độ nền.
  • Không có một video nào đ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 đang hiển thị trên màn hình cùng một lúc.
  • Bạn đang phát một video giới thiệu hoặc giải thích một lần mà bạn dự kiến người dùng sẽ chủ động xem mà không cần các chế độ điều khiển phát bên ngoài.
  • Nội dung của bạn liên quan đến 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 ý với việc ứ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ó, bạn nên chọn MediaSession. Nếu câu trả lời là không, bạn có thể muốn sử dụng Player.

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

Phiên 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 chạy 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 liên kết với phiên phát đó.

Để tạo một phiên phát nội dung đa phương tiện, hãy khởi chạy 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 đa phương tiện bằng trạng thái của trình phát. Do đó, bạn không cần phải xử lý việc liên kết từ người chơi đến 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, trong đó bạn cần tạo và duy trì PlaybackState độ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 hoạt động 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 ứng dụng chỉ có ý định tạo một phiên bản thể hiện duy nhất, đây là trường hợp phổ biến nhất.

Nếu muốn quản lý nhiều phiên bản phiên cùng một lúc, ứ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 IllegalStateException gặp sự cố với ứng dụng của mình kèm theo thông báo lỗi IllegalStateException: Session ID must be unique. ID=, thì có thể 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ã nhận dạng được phát hành. Để tránh các phiên bị rò rỉ do lỗi lập trình, các trường hợp như vậy sẽ được phát hiện và thông báo bằng cách gửi 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à yếu tố chính để kiểm soát quá trình phát. Lớp 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 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ý 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 như hướng dẫn "tạm dừng" cho Trợ lý Google. Tương tự, bạn nên cấp quyền truy cập vào hệ thống Android để hỗ trợ các chế độ điều khiển thông báo và màn hình khoá, hoặc cấp quyền truy cập vào đồng hồ Wear OS để có thể điều khiển chế độ phát từ mặt đồng hồ. Ứ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 lệnh phát cho ứng dụng đa phương tiện. Các lệnh này được phiên phát nội dung nghe nhìn nhận và cuối cùng 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 hỗ trợ việc truyền lệnh từ các nguồn bên ngoài đến phiên phát nội dung nghe nhìn.

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

Sau khi kết nối, tay điều khiển có thể gửi lệnh phát đến phiên. Sau đó, phiên sẽ uỷ quyền các lệnh đó cho người chơi. 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. Tương tự như vậy, các lệnh gọi lại này 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

Mộ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 hướng dẫn về danh sách phát của ExoPlayer. Trình điều khiển cũng có thể sửa đổi danh sách phát nếu trình điều khiển có COMMAND_SET_MEDIA_ITEM hoặc COMMAND_CHANGE_MEDIA_ITEMS.

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 các thực thể MediaItemURI đã xác định để có thể 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 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 tay đ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 đây đượ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 do người chơi phát trực tiếp.
  • MediaItem.RequestMetadata.searchQuery: Cụm từ tìm kiếm dạng văn bản, ví dụ: từ Trợ lý Google.
  • MediaItem.MediaMetadata: Siêu dữ liệu có cấu trúc như "tiêu đề" hoặc "nghệ sĩ".

Để có thêm các tuỳ chọn tuỳ chỉnh cho danh sách phát hoàn toàn mới, bạn có thể ghi đè onSetMediaItems() để 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 đã 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 tại chỉ mục của mục đã yêu cầu ban đầu. Bạn có thể tìm thấy ví dụ về cách triển khai onSetMediaItems() bằng tính năng này trong ứng dụng minh hoạ phiên.

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

Mỗi bộ điều khiển, chẳng hạn như Giao diện người dùng hệ thống, Android Auto hoặc Wear OS, có thể tự đưa ra quyết định về nút nào sẽ hiển thị cho người dùng. Để cho biết bạn muốn hiển thị chế độ điều khiển phát nào 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 tuỳ chọn này bao gồm một danh sách thứ tự các thực thể CommandButton, mỗi thực thể xác định một tuỳ chọn cho một nút trong giao diện người dùng.

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

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

  1. Biểu tượng, xác định giao diện hình ảnh. 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 trình đ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 trình điều khiển. Nếu không có hằng số biểu tượng nào được xác định trước phù hợp với trường hợp sử dụng của bạn, bạn có thể sử dụng setCustomIconResId.
  2. 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 được xác định trước hoặc tuỳ chỉnh.
  3. Khe, xác định vị trí đặt nút trong giao diện người dùng của tay điều khiển. Trường này không bắt buộc và được đặt tự động dựa trên Biểu tượngLệnh. Ví dụ: bạn có thể chỉ định nút sẽ hiển thị trong khu vực điều hướng "tiến" của giao diện người dùng thay vì khu vực "lưu lượng dữ liệu vượt quá" mặc định.

Kotlin

val button =
  CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD_15)
    .setSessionCommand(SessionCommand(CUSTOM_ACTION_ID, Bundle.EMPTY))
    .setSlots(CommandButton.SLOT_FORWARD)
    .build()

Java

CommandButton button =
    new CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD_15)
        .setSessionCommand(new SessionCommand(CUSTOM_ACTION_ID, Bundle.EMPTY))
        .setSlots(CommandButton.SLOT_FORWARD)
        .build();

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

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

Bạn có thể sử dụng CommandButton.DisplayConstraints để tạo bản xem trước cách giải quyết các tuỳ chọn nút phát trung gian tuỳ thuộc vào các quy tắc ràng buộc hiển thị giao diện người dùng.

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

Cách dễ nhất để đặt lựa chọn ưu tiên về nút phát 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 các tuỳ chọn nút đa phương tiện cho từng tay điều khiển đã 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 phá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 người chơi, bạn nên cập nhật các nút hiển thị trong giao diện người dùng của tay đ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 liên kết với nút này. Để cập nhật lựa chọn ưu tiên về nút phát, 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 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 hiện có của người chơi bằng các lệnh tuỳ chỉnh và cũng có thể chặn các lệnh của người chơi và nút nội dung nghe nhìn sắp tới để thay đổi hành vi mặc định.

Khai báo và xử lý 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, chẳng hạn như có thể được sử dụng trong lựa chọn ưu tiên về nút đa phương tiện. Ví dụ: bạn có thể 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 tay điều khiển đã kết nối.

Kotlin

private class CustomMediaSessionCallback: MediaSession.Callback {
  // Configure commands available to the controller in onConnect()
  override fun onConnect(
    session: MediaSession,
    controller: MediaSession.ControllerInfo
  ): MediaSession.ConnectionResult {
    val sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon()
        .add(SessionCommand(SAVE_TO_FAVORITES, Bundle.EMPTY))
        .build()
    return AcceptedResultBuilder(session)
        .setAvailableSessionCommands(sessionCommands)
        .build()
  }
}

Java

class CustomMediaSessionCallback implements MediaSession.Callback {
  // Configure commands available to the controller in onConnect()
  @Override
  public ConnectionResult onConnect(
    MediaSession session,
    ControllerInfo controller) {
    SessionCommands sessionCommands =
        ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon()
            .add(new SessionCommand(SAVE_TO_FAVORITES, new Bundle()))
            .build();
    return new AcceptedResultBuilder(session)
        .setAvailableSessionCommands(sessionCommands)
        .build();
  }
}

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

Kotlin

private class CustomMediaSessionCallback: MediaSession.Callback {
  ...
  override fun onCustomCommand(
    session: MediaSession,
    controller: MediaSession.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)
      )
    }
    ...
  }
}

Java

class CustomMediaSessionCallback 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)
      );
    }
    ...
  }
}

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 lệnh mặc định của người chơi

Tất cả các lệnh mặc định và 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 player = (logic to build a Player instance)

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

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

Java

ExoPlayer player = (logic to build a Player instance)

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 của ExoPlayer về Tuỳ chỉnh.

Xác định tay điều khiển yêu cầu của lệnh người chơi

Khi 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

class CallerAwarePlayer(player: Player) :
  ForwardingSimpleBasePlayer(player) {

  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

public class CallerAwarePlayer extends ForwardingSimpleBasePlayer {
  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 cách xử lý nút phát nội dung đa phương tiện

Nút phát 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 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ả sự kiện nút phát nội dung đa phương tiện sắp tới 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, bạn có thể chặn các sự kiện nút phát nội dung đa phương tiện trong MediaSession.Callback.onMediaButtonEvent(Intent).

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

Có hai loại lỗi mà một 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 sẽ làm gián đoạn quá trình phát. Lỗi nghiêm trọng sẽ được tự động báo cáo cho trình điều khiển khi xảy ra. Lỗi không nghiêm trọng là lỗi không liên quan đến 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 tay điều khiển theo cách thủ công.

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

Trình phát sẽ báo cáo lỗi phát nghiêm trọng cho phiên, sau đó 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 sẽ chuyển sang STATE_IDLEMediaController.getPlaybackError() sẽ trả về PlaybackException đã gây ra quá trình chuyển đổi. Trình điều khiển có thể kiểm tra PlayerException.errorCode để lấy thông tin về lý do gây ra lỗi.

Để có khả năng tương tác, lỗi nghiêm trọng sẽ được sao chép vào phiên nền tảng bằng cách chuyển trạng thái của phiên đó thành STATE_ERROR và đặt mã lỗi và thông báo lỗi theo PlaybackException.

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

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

Kotlin

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

Java

Player forwardingPlayer = new ErrorForwardingPlayer(player);
MediaSession session =
    new MediaSession.Builder(context, forwardingPlayer).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ự như vậy, 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

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
  }

  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

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 lỗi không nghiêm trọng không bắt nguồn từ một ngoại lệ kỹ thuật đến tất cả hoặc một tay đ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 trình điều khiển thông báo đa phương tiện, mã lỗi và thông báo lỗi sẽ được sao chép vào phiên đa phương tiện của nền tảng, trong khi PlaybackState.state không được thay đổi thành STATE_ERROR.

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

MediaController nhận được 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.
              }
            });