미디어 앱에 연결하는 방법에는 두 가지가 있습니다.
MediaControllerMediaBrowser
MediaController
미디어 컨트롤러는 미디어 세션과 상호작용하여 미디어 앱의 재생을 쿼리하고 제어합니다. Media3에서 MediaController
API는 Player 인터페이스를 구현합니다. 미디어 컨트롤러를 사용하는 클라이언트 앱의 예는 다음과 같습니다.
- Android 시스템 미디어 컨트롤
- Android Wear OS 호환 앱
- Android Auto 및 Automotive OS
- Google 어시스턴트와 같은 음성 지원
- 미디어 컨트롤러 테스트 앱
미디어 컨트롤러는 미디어 앱 내에서도 유용할 수 있습니다. 예를 들어 플레이어와 미디어 세션이 UI가 있는 Activity 또는 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()가 있습니다.
MediaController.Listener는 Builder를 사용하여 컨트롤러를 빌드할 때 설정할 수 있습니다.
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();
다른 구성요소와 마찬가지로 Activity 또는 Fragment의 onStop() 메서드와 같이 더 이상 필요하지 않은 경우 MediaController를 해제해야 합니다.
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) }, ) }
자바
// 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)); }