連線至媒體應用程式

連線至媒體應用程式的方式有兩種:

  1. MediaController
  2. MediaBrowser

MediaController

媒體控制器會與媒體工作階段互動,查詢及控制媒體應用程式的播放。在 Media3 中,MediaController API 會實作 Player 介面。使用媒體控制器的用戶端應用程式範例包括:

媒體控制器在媒體應用程式中也很有用,例如播放器和媒體工作階段位於與 Activity 或具有 UI 的 Fragment 分開的 Service 中。

建立 MediaController

如要建立 MediaController,請先為對應的 MediaSession 建立 SessionToken。Activity 或 Fragment 的 onStart() 方法可能適合執行這項操作。

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 時釋放它,例如在 Activity 或 Fragment 的 onStop() 方法中。

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

顯示其他媒體應用程式的播放控制項

顯示含有其他媒體應用程式按鈕的 UI 控制項時,請務必遵循該應用程式宣告的媒體按鈕偏好設定。

如要根據 UI 的限制和需求解析應用程式偏好設定,請使用 CommandButton.DisplayConstraints。您可以定義 UI 的限制和限制,而 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));
}