이 SessionToken를 사용하여 MediaController를 빌드하면 컨트롤러가 지정된 세션에 연결됩니다. 이는 비동기적으로 발생하므로 결과를 수신 대기하고 사용 가능한 경우 사용해야 합니다.
Kotlin
valcontrollerFuture=MediaController.Builder(context,sessionToken).buildAsync()controllerFuture.addListener({// MediaController is available here with controllerFuture.get()},MoreExecutors.directExecutor())
자바
ListenableFuture<MediaController>controllerFuture=newMediaController.Builder(context,sessionToken).buildAsync();controllerFuture.addListener(()->{// MediaController is available here with controllerFuture.get()},MoreExecutors.directExecutor());
MediaController 사용
MediaController는 Player 인터페이스를 구현하므로 인터페이스에 정의된 명령어를 사용하여 연결된 MediaSession의 재생을 제어할 수 있습니다.
즉, MediaController에서 play()를 호출하면 연결된 MediaSession에 명령어가 전송되고, MediaSession는 이후에 기본 Player에 명령어를 위임합니다.
Player 상태의 변경사항을 수신 대기하도록 컨트롤러에 Player.Listener를 추가할 수 있습니다. Player.Listener 사용에 관한 자세한 내용은 플레이어 이벤트 가이드를 참고하세요.
다른 구성요소와 마찬가지로 Activity 또는 Fragment의 onStop() 메서드와 같이 더 이상 필요하지 않을 때 MediaController를 해제해야 합니다.
Kotlin
MediaController.releaseFuture(controllerFuture)
자바
MediaController.releaseFuture(controllerFuture);
컨트롤러를 해제해도 세션에 전송된 모든 대기 중인 명령어가 전송되며 이러한 명령어가 처리된 후 또는 제한 시간 기간이 지난 후에만 세션 서비스에서 바인딩이 해제됩니다(둘 중 먼저 발생하는 경우).
MediaBrowser
MediaBrowser는 MediaController에서 제공하는 기능을 기반으로 미디어 앱의 MediaLibraryService에서 제공하는 미디어 라이브러리도 탐색할 수 있도록 합니다.
MediaBrowser를 만드는 방법
Kotlin
valbrowserFuture=MediaBrowser.Builder(context,sessionToken).buildAsync()browserFuture.addListener({// MediaBrowser is available here with browserFuture.get()},MoreExecutors.directExecutor())
자바
ListenableFuture<MediaBrowser>browserFuture=newMediaBrowser.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.valrootFuture=mediaBrowser.getLibraryRoot(/* params= */null)rootFuture.addListener({// Root node MediaItem is available here with rootFuture.get().value},MoreExecutors.directExecutor())
자바
// 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.valchildrenFuture=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())
자바
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 메서드는 아이콘, 위치, 의도된 작업과 함께 표시할 버튼의 명확한 목록을 제공합니다.
이 페이지에 나와 있는 콘텐츠와 코드 샘플에는 콘텐츠 라이선스에서 설명하는 라이선스가 적용됩니다. 자바 및 OpenJDK는 Oracle 및 Oracle 계열사의 상표 또는 등록 상표입니다.
최종 업데이트: 2025-08-27(UTC)
[[["이해하기 쉬움","easyToUnderstand","thumb-up"],["문제가 해결됨","solvedMyProblem","thumb-up"],["기타","otherUp","thumb-up"]],[["필요한 정보가 없음","missingTheInformationINeed","thumb-down"],["너무 복잡함/단계 수가 너무 많음","tooComplicatedTooManySteps","thumb-down"],["오래됨","outOfDate","thumb-down"],["번역 문제","translationIssue","thumb-down"],["샘플/코드 문제","samplesCodeIssue","thumb-down"],["기타","otherDown","thumb-down"]],["최종 업데이트: 2025-08-27(UTC)"],[],[],null,["# Connect to a media app\n\nThere are two ways to connect to a Media app:\n\n1. `MediaController`\n2. `MediaBrowser`\n\n`MediaController`\n-----------------\n\nA media controller interacts with a media session to query and control a media\napp's playback. In Media3, the [`MediaController`](/reference/kotlin/androidx/media3/session/MediaController)\nAPI implements the `Player` interface. Examples of client apps that use a media\ncontroller include:\n\n- [Android system media controls](/media/implement/surfaces/mobile)\n- Android Wear OS companion app\n- [Android Auto and Automotive OS](/training/cars/media)\n- Voice assistants, like [Google Assistant](/media/implement/assistant)\n- The [Media Controller Test app](/media/optimize/mct)\n\nA media controller can also be useful within a media app, for example if the\nplayer and media session live in a `Service` separate from the `Activity` or\n`Fragment` with the UI.\n\n### Create a `MediaController`\n\nTo create a `MediaController`, start by creating a `SessionToken` for the\ncorresponding `MediaSession`. The `onStart()` method of your `Activity` or\n`Fragment` can be a good place for this. \n\n### Kotlin\n\n val sessionToken =\n SessionToken(context, ComponentName(context, PlaybackService::class.java))\n\n### Java\n\n SessionToken sessionToken =\n new SessionToken(context, new ComponentName(context, PlaybackService.class));\n\n\u003cbr /\u003e\n\nUsing this `SessionToken` to then build a `MediaController` connects the\ncontroller to the given session. This takes place asynchronously, so you should\nlisten for the result and use it when available. \n\n### Kotlin\n\n val controllerFuture =\n MediaController.Builder(context, sessionToken).buildAsync()\n controllerFuture.addListener({\n // MediaController is available here with controllerFuture.get()\n }, MoreExecutors.directExecutor())\n\n### Java\n\n ListenableFuture\u003cMediaController\u003e controllerFuture =\n new MediaController.Builder(context, sessionToken).buildAsync();\n controllerFuture.addListener(() -\u003e {\n // MediaController is available here with controllerFuture.get()\n }, MoreExecutors.directExecutor());\n\n\u003cbr /\u003e\n\n| **Note:** Use `ContextCompat.getMainExecutor()` instead of `MoreExecutors.directExecutor()` in case you run your service in another process than the UI.\n| **Note:** Apps targeting SDK level 30 or higher must include a `\u003cqueries\u003e` element in their manifest to connect to a different app's `MediaSessionService` (or similar component). Refer to the [package visibility](/training/package-visibility) guide for further details.\n\n### Use a `MediaController`\n\n`MediaController` implements the `Player` interface, so you can use the commands\ndefined in the interface to control playback of the connected `MediaSession`.\nThis is to say that calling `play()` on a `MediaController` will send the\ncommand to the connected `MediaSession`, which will subsequently delegate the\ncommand to its underlying `Player`.\n| **Tip:** A media controller implements the `Player` interface, and as such you can use it within your UI as a `Player`.\n\nYou can add a `Player.Listener` to the controller to listen for changes in the\n`Player` state. Refer to the [Player events](/guide/topics/media/exoplayer/listening-to-player-events) guide for more details on using a\n`Player.Listener`.\n\nThe `MediaController.Listener` interface defines additional callbacks for events\nand custom commands from the connected `MediaSession`. Examples are\n[`onCustomCommand()`](/reference/kotlin/androidx/media3/session/MediaController.Listener#onCustomCommand(androidx.media3.session.MediaController,androidx.media3.session.SessionCommand,android.os.Bundle)) when the session sends a custom command,\n[`onAvailableSessionCommandsChanged()`](/reference/kotlin/androidx/media3/session/MediaController.Listener#onAvailableSessionCommandsChanged(androidx.media3.session.MediaController,androidx.media3.session.SessionCommands)) when the session changes the available\nsession commands or [`onDisconnected()`](/reference/kotlin/androidx/media3/session/MediaController.Listener#onDisconnected(androidx.media3.session.MediaController)) when the controller is disconnected\nfrom the session.\n\nA `MediaController.Listener` can be set when building the controller with a\n`Builder`: \n\n### Kotlin\n\n MediaController.Builder(context, sessionToken)\n .setListener(\n object : MediaController.Listener {\n override fun onCustomCommand(\n controller: MediaController,\n command: SessionCommand,\n args: Bundle,\n ): ListenableFuture\u003cSessionResult\u003e {\n // Handle custom command.\n return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))\n }\n\n override fun onDisconnected(controller: MediaController) {\n // Handle disconnection.\n }\n }\n )\n .buildAsync()\n\n### Java\n\n new MediaController.Builder(context, sessionToken)\n .setListener(\n new MediaController.Listener() {\n @Override\n public ListenableFuture\u003cSessionResult\u003e onCustomCommand(\n MediaController controller, SessionCommand command, Bundle args) {\n // Handle custom command.\n return Futures.immediateFuture(new SessionResult(SessionResult.RESULT_SUCCESS));\n }\n\n @Override\n public void onDisconnected(MediaController controller) {\n // Handle disconnection.\n }\n })\n .buildAsync();\n\n\u003cbr /\u003e\n\nAs with other components, remember to release the `MediaController` when it is\nno longer needed, such as in the `onStop()` method of an `Activity` or\n`Fragment`. \n\n### Kotlin\n\n MediaController.releaseFuture(controllerFuture)\n\n### Java\n\n MediaController.releaseFuture(controllerFuture);\n\n\u003cbr /\u003e\n\nReleasing the controller will still deliver all pending commands sent to the\nsession and only unbind from the session service either once these commands have\nbeen handled or after a timeout period, whichever occurs first.\n\n`MediaBrowser`\n--------------\n\nA `MediaBrowser` builds on top of the capabilities offered by a\n`MediaController` to also enable browsing the media library offered by a media\napp's `MediaLibraryService`.\n\n### Create a `MediaBrowser`\n\n### Kotlin\n\n val browserFuture = MediaBrowser.Builder(context, sessionToken).buildAsync()\n browserFuture.addListener({\n // MediaBrowser is available here with browserFuture.get()\n }, MoreExecutors.directExecutor())\n\n### Java\n\n ListenableFuture\u003cMediaBrowser\u003e browserFuture =\n new MediaBrowser.Builder(context, sessionToken).buildAsync();\n browserFuture.addListener(() -\u003e {\n // MediaBrowser is available here with browserFuture.get()\n }, MoreExecutors.directExecutor());\n\n\u003cbr /\u003e\n\n### Use a `MediaBrowser`\n\nTo start browsing the media app's content library, first retrieve the root node\nwith `getLibraryRoot()`: \n\n### Kotlin\n\n // Get the library root to start browsing the library tree.\n val rootFuture = mediaBrowser.getLibraryRoot(/* params= */ null)\n rootFuture.addListener({\n // Root node MediaItem is available here with rootFuture.get().value\n }, MoreExecutors.directExecutor())\n\n### Java\n\n // Get the library root to start browsing the library tree.\n ListenableFuture\u003cLibraryResult\u003cMediaItem\u003e\u003e rootFuture =\n mediaBrowser.getLibraryRoot(/* params= */ null);\n rootFuture.addListener(() -\u003e {\n // Root node MediaItem is available here with rootFuture.get().value\n }, MoreExecutors.directExecutor());\n\n\u003cbr /\u003e\n\nYou can then navigate through the media library by retrieving the children of a\n`MediaItem` in the library with `getChildren()`. For example, to retrieve the\nchildren of the root node `MediaItem`: \n\n### Kotlin\n\n // Get the library root to start browsing the library tree.\n val childrenFuture = \n mediaBrowser.getChildren(rootMediaItem.mediaId, 0, Int.MAX_VALUE, null)\n childrenFuture.addListener({\n // List of children MediaItem nodes is available here with\n // childrenFuture.get().value\n }, MoreExecutors.directExecutor())\n\n### Java\n\n ListenableFuture\u003cLibraryResult\u003cImmutableList\u003cMediaItem\u003e\u003e\u003e childrenFuture =\n mediaBrowser.getChildren(rootMediaItem.mediaId, 0, Integer.MAX_VALUE, null);\n childrenFuture.addListener(() -\u003e {\n // List of children MediaItem nodes is available here with\n // childrenFuture.get().value\n }, MoreExecutors.directExecutor());\n\n\u003cbr /\u003e\n\nDisplay playback controls for another media app\n-----------------------------------------------\n\nWhen displaying UI controls with buttons for another media app, it's important\nto follow the declared [media button preferences](/media/media3/session/control-playback#commands) of that\napp.\n\nThe best way to resolve the preferences of the app and the constraints and\nrequirements of your UI, is to use `CommandButton.DisplayConstraints`. You can\ndefine limits and restrictions of your UI can do, and the\n[`resolve`](/reference/kotlin/androidx/media3/session/CommandButton.DisplayConstraints#resolve(java.util.List%3Candroidx.media3.session.CommandButton%3E,androidx.media3.common.Player)) method provides a definite list of buttons to\ndisplay with their icon, position and intended action."]]