媒體應用程式通常包含媒體項目集合,並以階層方式整理。例如專輯中的歌曲或播放清單中的電視節目集數。這個媒體項目階層稱為媒體庫。
MediaLibraryService
提供標準化 API,可服務及存取媒體庫。舉例來說,在媒體應用程式中新增 Android Auto 支援時,這項功能就非常實用,因為 Android Auto 會為媒體庫提供專屬的駕駛人安全 UI。
建構 MediaLibraryService
實作 MediaLibraryService
的方式與實作 MediaSessionService
類似,但您應在 onGetSession()
方法中傳回 MediaLibrarySession
,而非 MediaSession
。
Kotlin
class PlaybackService : MediaLibraryService() { var mediaLibrarySession: MediaLibrarySession? = null var callback: MediaLibrarySession.Callback = object : MediaLibrarySession.Callback {...} // If desired, validate the controller before returning the media library session override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaLibrarySession? = mediaLibrarySession // Create your player and media library session in the onCreate lifecycle event override fun onCreate() { super.onCreate() val player = ExoPlayer.Builder(this).build() mediaLibrarySession = MediaLibrarySession.Builder(this, player, callback).build() } // Remember to release the player and media library session in onDestroy override fun onDestroy() { mediaLibrarySession?.run { player.release() release() mediaLibrarySession = null } super.onDestroy() } }
Java
class PlaybackService extends MediaLibraryService { MediaLibrarySession mediaLibrarySession = null; MediaLibrarySession.Callback callback = new MediaLibrarySession.Callback() {...}; @Override public MediaLibrarySession onGetSession(MediaSession.ControllerInfo controllerInfo) { // If desired, validate the controller before returning the media library session return mediaLibrarySession; } // Create your player and media library session in the onCreate lifecycle event @Override public void onCreate() { super.onCreate(); ExoPlayer player = new ExoPlayer.Builder(this).build(); mediaLibrarySession = new MediaLibrarySession.Builder(this, player, callback).build(); } // Remember to release the player and media library session in onDestroy @Override public void onDestroy() { if (mediaLibrarySession != null) { mediaLibrarySession.getPlayer().release(); mediaLibrarySession.release(); mediaLibrarySession = null; } super.onDestroy(); } }
請記得在資訊清單檔案中宣告 Service
和必要權限:
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/>
<action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
使用 MediaLibrarySession
MediaLibraryService
API 預期媒體庫會以樹狀結構格式建構,包含單一根節點和子節點,這些子節點可能可播放或可進一步瀏覽。
MediaLibrarySession
會擴充 MediaSession
API,加入內容瀏覽 API。相較於 MediaSession
回呼,MediaLibrarySession
回呼新增了下列方法:
onGetLibraryRoot()
當用戶端要求內容樹狀結構的根MediaItem
時onGetChildren()
for when a client requests the children of aMediaItem
in the content treeonGetSearchResult()
用戶端針對特定查詢要求內容樹狀結構的搜尋結果時
相關回呼方法會包含 LibraryParams
物件,其中含有用戶端應用程式感興趣的內容樹狀結構類型相關額外信號。
媒體項目的指令按鈕
工作階段應用程式可以宣告 MediaItem
支援的指令按鈕 (位於 MediaMetadata
中)。這樣一來,控制器就能將一或多個 CommandButton
項目指派給媒體項目,並顯示這些項目,以及將項目的自訂指令傳送至工作階段。
在工作階段端設定指令按鈕
建構工作階段時,工作階段應用程式會宣告工作階段可做為自訂指令處理的指令按鈕集:
Kotlin
val allCommandButtons = listOf( CommandButton.Builder(CommandButton.ICON_PLAYLIST_ADD) .setDisplayName(context.getString(R.string.add_to_playlist)) .setSessionCommand(SessionCommand(COMMAND_PLAYLIST_ADD, Bundle.EMPTY)) .setExtras(playlistAddExtras) .build(), CommandButton.Builder(CommandButton.ICON_RADIO) .setDisplayName(context.getString(R.string.radio_station)) .setSessionCommand(SessionCommand(COMMAND_RADIO, Bundle.EMPTY)) .setExtras(radioExtras) .build(), // possibly more here ) // Add all command buttons for media items supported by the session. val session = MediaSession.Builder(context, player) .setCommandButtonsForMediaItems(allCommandButtons) .build()
Java
ImmutableList<CommandButton> allCommandButtons = ImmutableList.of( new CommandButton.Builder(CommandButton.ICON_PLAYLIST_ADD) .setDisplayName(context.getString(R.string.add_to_playlist)) .setSessionCommand(new SessionCommand(COMMAND_PLAYLIST_ADD, Bundle.EMPTY)) .setExtras(playlistAddExtras) .build(), new CommandButton.Builder(CommandButton.ICON_RADIO) .setDisplayName(context.getString(R.string.radio_station)) .setSessionCommand(new SessionCommand(COMMAND_RADIO, Bundle.EMPTY)) .setExtras(radioExtras) .build()); // Add all command buttons for media items supported by the session. MediaSession session = new MediaSession.Builder(context, player) .setCommandButtonsForMediaItems(allCommandButtons) .build();
建構媒體項目時,工作階段應用程式可以新增一組支援的指令 ID,參照建構工作階段時設定的指令按鈕工作階段指令:
Kotlin
val mediaItem = MediaItem.Builder() .setMediaMetadata( MediaMetadata.Builder() .setSupportedCommands(listOf(COMMAND_PLAYLIST_ADD, COMMAND_RADIO)) .build()) .build()
Java
MediaItem mediaItem = new MediaItem.Builder() .setMediaMetadata( new MediaMetadata.Builder() .setSupportedCommands(ImmutableList.of(COMMAND_PLAYLIST_ADD, COMMAND_RADIO)) .build()) .build();
當控制器或瀏覽器連線或呼叫工作階段的其他方法時Callback
,工作階段應用程式可以檢查傳遞至回呼的 ControllerInfo
,取得控制器或瀏覽器可顯示的指令按鈕數量上限。傳遞至回呼方法的 ControllerInfo
提供 getter,可方便存取這個值。預設值為 0,表示瀏覽器或控制器不支援這項功能:
Kotlin
override fun onGetItem( session: MediaLibrarySession, browser: MediaSession.ControllerInfo, mediaId: String, ): ListenableFuture<LibraryResult<MediaItem>> { val settableFuture = SettableFuture.create<LibraryResult<MediaItem>>() val maxCommandsForMediaItems = browser.maxCommandsForMediaItems scope.launch { loadMediaItem(settableFuture, mediaId, maxCommandsForMediaItems) } return settableFuture }
Java
@Override public ListenableFuture<LibraryResult<MediaItem>> onGetItem( MediaLibraryService.MediaLibrarySession session, ControllerInfo browser, String mediaId) { SettableFuture<LibraryResult<MediaItem>> settableFuture = SettableFuture.create(); int maxCommandsForMediaItems = browser.getMaxCommandsForMediaItems(); loadMediaItemAsync(settableFuture, mediaId, maxCommandsForMediaItems); return settableFuture; }
處理為媒體項目傳送的自訂動作時,工作階段應用程式可以從傳遞至 onCustomCommand
的引數 Bundle
取得媒體項目 ID:
Kotlin
override fun onCustomCommand( session: MediaSession, controller: MediaSession.ControllerInfo, customCommand: SessionCommand, args: Bundle, ): ListenableFuture<SessionResult> { val mediaItemId = args.getString(MediaConstants.EXTRA_KEY_MEDIA_ID) return if (mediaItemId != null) handleCustomCommandForMediaItem(controller, customCommand, mediaItemId, args) else handleCustomCommand(controller, customCommand, args) }
Java
@Override public ListenableFuture<SessionResult> onCustomCommand( MediaSession session, ControllerInfo controller, SessionCommand customCommand, Bundle args) { String mediaItemId = args.getString(MediaConstants.EXTRA_KEY_MEDIA_ID); return mediaItemId != null ? handleCustomCommandForMediaItem(controller, customCommand, mediaItemId, args) : handleCustomCommand(controller, customCommand, args); }
將指令按鈕當做瀏覽器或控制器使用
在 MediaController
端,應用程式建構 MediaController
或 MediaBrowser
時,可以為媒體項目宣告支援的指令按鈕數量上限:
Kotlin
val browserFuture = MediaBrowser.Builder(context, sessionToken) .setMaxCommandsForMediaItems(3) .buildAsync()
Java
ListenableFuture<MediaBrowser> browserFuture = new MediaBrowser.Builder(context, sessionToken) .setMaxCommandsForMediaItems(3) .buildAsync();
連線至工作階段後,控制器應用程式可以接收媒體項目支援的指令按鈕,以及控制器已獲得工作階段應用程式授予的可用指令:
Kotlin
val commandButtonsForMediaItem: List<CommandButton> = controller.getCommandButtonsForMediaItem(mediaItem)
Java
ImmutableList<CommandButton> commandButtonsForMediaItem = controller.getCommandButtonsForMediaItem(mediaItem);
為方便起見,MediaController
可透過 MediaController.sendCustomCommand(SessionCommand, MediaItem, Bundle)
傳送媒體項目專屬的自訂指令:
Kotlin
controller.sendCustomCommand(addToPlaylistButton.sessionCommand!!, mediaItem, Bundle.EMPTY)
Java
controller.sendCustomCommand( checkNotNull(addToPlaylistButton.sessionCommand), mediaItem, Bundle.EMPTY);