连接到媒体应用

您可以通过以下两种方式连接到媒体应用:

  1. MediaController
  2. MediaBrowser

MediaController

媒体控制器与媒体会话交互,以查询和控制媒体应用的播放。在 Media3 中,MediaController API 实现了 Player 接口。使用媒体控制器的客户端应用示例包括:

媒体控制器在媒体应用中也可能很有用,例如,如果播放器和媒体会话位于与界面所在的 ActivityFragment 分开的 Service 中。

创建 MediaController

如需创建 MediaController,请先为相应的 MediaSession 创建 SessionTokenActivityFragmentonStart() 方法可能是一个不错的选择。

Kotlin

val sessionToken = SessionToken(context, ComponentName(context, PlaybackService::class.java))

Java

SessionToken sessionToken =
    new SessionToken(context, new ComponentName(context, PlaybackService.class));

然后使用此 SessionToken 构建 MediaController,将控制器连接到给定的会话。此过程是异步的,因此您应监听结果并在结果可用时使用它。

Kotlin

val controllerFuture = MediaController.Builder(context, sessionToken).buildAsync()
controllerFuture.addListener(
  {
    // MediaController is available here with controllerFuture.get()
  },
  MoreExecutors.directExecutor(),
)

Java

ListenableFuture<MediaController> controllerFuture =
    new MediaController.Builder(context, sessionToken).buildAsync();
controllerFuture.addListener(
    () -> {
      // MediaController is available here with controllerFuture.get()
    },
    MoreExecutors.directExecutor());

使用 MediaController

MediaController 实现了 Player 接口,因此您可以使用该接口中定义的命令来控制已连接的 MediaSession 的播放。 也就是说,对 MediaController 调用 play() 会将命令发送到已连接的 MediaSession,后者随后会将该命令委托给其底层 Player

您可以向控制器添加 Player.Listener,以监听 Player 状态的变化。如需详细了解如何使用 Player.Listener,请参阅播放器事件指南。

MediaController.Listener 接口为来自已连接的 MediaSession 的事件和自定义命令定义了其他回调。例如,当会话发送自定义命令时, onCustomCommand();当会话更改可用 会话命令时, onAvailableSessionCommandsChanged();当控制器与会话断开连接时,onDisconnected()

您可以在使用 Builder 构建控制器时设置 MediaController.Listener

Kotlin

val controllerFuture =
  MediaController.Builder(context, sessionToken)
    .setListener(
      object : MediaController.Listener {
        override fun onCustomCommand(
          controller: MediaController,
          command: SessionCommand,
          args: Bundle,
        ): ListenableFuture<SessionResult> {
          // Handle custom command.
          return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
        }

        override fun onDisconnected(controller: MediaController) {
          // Handle disconnection.
        }
      }
    )
    .buildAsync()

Java

ListenableFuture<MediaController> controllerFuture =
    new MediaController.Builder(context, sessionToken)
        .setListener(
            new MediaController.Listener() {
              @Override
              public ListenableFuture<SessionResult> onCustomCommand(
                  MediaController controller, SessionCommand command, Bundle args) {
                // Handle custom command.
                return Futures.immediateFuture(new SessionResult(SessionResult.RESULT_SUCCESS));
              }

              @Override
              public void onDisconnected(MediaController controller) {
                // Handle disconnection.
              }
            })
        .buildAsync();

与其他组件一样,请记得在不再需要 MediaController 时释放它,例如在 ActivityFragmentonStop() 方法中。

Kotlin

MediaController.releaseFuture(controllerFuture)

Java

MediaController.releaseFuture(controllerFuture);

释放控制器后,系统仍会传送发送到会话的所有待处理命令,并且仅在这些命令处理完毕后或超时后(以先发生者为准)与会话服务取消绑定。

MediaBrowser

MediaBrowser 基于 MediaController 提供的功能构建,还支持浏览媒体应用的 MediaLibraryService 提供的媒体库。

创建 MediaBrowser

Kotlin

val browserFuture = MediaBrowser.Builder(context, sessionToken).buildAsync()
browserFuture.addListener(
  {
    // MediaBrowser is available here with browserFuture.get()
  },
  MoreExecutors.directExecutor(),
)

Java

ListenableFuture<MediaBrowser> browserFuture =
    new MediaBrowser.Builder(context, sessionToken).buildAsync();
browserFuture.addListener(
    () -> {
      // MediaBrowser is available here with browserFuture.get()
    },
    MoreExecutors.directExecutor());

使用 MediaBrowser

如需开始浏览媒体应用的内容库,请先使用 getLibraryRoot() 检索根节点:

Kotlin

// Get the library root to start browsing the library tree.
val rootFuture = mediaBrowser.getLibraryRoot(/* params= */ null)
rootFuture.addListener(
  {
    // Root node MediaItem is available here with rootFuture.get().value
  },
  MoreExecutors.directExecutor(),
)

Java

// Get the library root to start browsing the library tree.
ListenableFuture<LibraryResult<MediaItem>> rootFuture =
    mediaBrowser.getLibraryRoot(/* params= */ null);
rootFuture.addListener(
    () -> {
      // Root node MediaItem is available here with rootFuture.get().value
    },
    MoreExecutors.directExecutor());

然后,您可以使用 getChildren() 检索库中 MediaItem 的子项,从而浏览媒体库。例如,如需检索根节点 MediaItem 的子项,请执行以下操作:

Kotlin

// Get the library root to start browsing the library tree.
val childrenFuture = mediaBrowser.getChildren(rootMediaItem.mediaId, 0, Int.MAX_VALUE, null)
childrenFuture.addListener(
  {
    // List of children MediaItem nodes is available here with
    // childrenFuture.get().value
  },
  MoreExecutors.directExecutor(),
)

Java

ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> childrenFuture =
    mediaBrowser.getChildren(rootMediaItem.mediaId, 0, Integer.MAX_VALUE, null);
childrenFuture.addListener(
    () -> {
      // List of children MediaItem nodes is available here with
      // childrenFuture.get().value
    },
    MoreExecutors.directExecutor());

显示其他媒体应用的播放控件

在显示包含其他媒体应用按钮的界面控件时,请务必 遵循该应用声明的 媒体按钮偏好设置

如需根据界面的限制和要求解析应用的偏好设置,请使用 CommandButton.DisplayConstraints。您可以定义界面可以执行的操作的限制,并且 resolve 方法会提供要显示的按钮的明确列表,其中包含按钮的图标、位置和预期操作。如果用户点击其中一个按钮,您可以使用 CommandButton.executeAction 触发媒体应用中的关联操作。

Kotlin

// Get media button preferences from media app
val mediaButtonPreferences = controller.getMediaButtonPreferences()
// Declare constraints of UI (example: limit overflow button to one)
val displayConstraints =
  DisplayConstraints.Builder().setMaxButtonsForSlot(CommandButton.SLOT_OVERFLOW, 1).build()
// Resolve media app preferences with constraints
val resolvedButtons = displayConstraints.resolve(mediaButtonPreferences, controller)
// Display buttons in UI
for (button in resolvedButtons) {
  generateUiButton(
    uiPosition = button.slots[0],
    icon = getIconRes(button.icon),
    onClick = { button.executeAction(controller) },
  )
}

Java

// Get media button preferences from media app
List<CommandButton> mediaButtonPreferences = controller.getMediaButtonPreferences();
// Declare constraints of UI (example: limit overflow button to one)
DisplayConstraints displayConstraints =
    new DisplayConstraints.Builder()
        .setMaxButtonsForSlot(CommandButton.SLOT_OVERFLOW, 1)
        .build();
// Resolve media app preferences with constraints
List<CommandButton> resolvedButtons =
    displayConstraints.resolve(mediaButtonPreferences, controller);
// Display buttons in UI
for (CommandButton button : resolvedButtons) {
  generateUiButton(
      /* uiPosition= */ button.slots.get(0),
      /* icon= */ getIconRes(button.icon),
      /* onClick= */ () -> button.executeAction(controller));
}