Medien-Apps enthalten oft Sammlungen von Mediendateien, die in einer Hierarchie organisiert sind. Beispiele: Songs in einem Album oder TV-Folgen in einer Playlist. Diese Hierarchie von Mediendateien wird als Mediathek bezeichnet.
Ein MediaLibraryService bietet eine standardisierte API, um Ihre Mediathek bereitzustellen und darauf zuzugreifen. Das kann beispielsweise hilfreich sein, wenn du deiner Medien-App Unterstützung für Android Auto hinzufügst, das eine eigene, für Autofahrer sichere Benutzeroberfläche für deine Mediathek bietet.
MediaLibraryService erstellen
Die Implementierung eines MediaLibraryService ähnelt der Implementierung eines MediaSessionService, mit dem Unterschied, dass Sie in der Methode onGetSession() einen MediaLibrarySession anstelle eines MediaSession zurückgeben sollten.
Kotlin
class PlaybackService : MediaLibraryService() { private var mediaLibrarySession: MediaLibrarySession? = null private val callback: MediaLibrarySession.Callback = object : MediaLibrarySession.Callback { /* ... */ } override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaLibrarySession? { // 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 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 und die erforderlichen Berechtigungen in der Manifestdatei zu deklarieren:
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaLibraryService"/>
<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" />
Es wird empfohlen, den Dienst sowohl als Plattform- als auch als Media3-Dienstschnittstelle zu registrieren:
Aus Gründen der Kompatibilität mit Clients, die die MediaSession APIs der Plattform verwenden, wird empfohlen,
<action android:name="android.media.browse.MediaBrowserService"/>in dasintent-filter-Element aufzunehmen. Media3 bietet automatisch Abwärtskompatibilität für diese Dienstschnittstelle.Als zukunftssichere Einrichtung und um sicherzustellen, dass Apps, die Media3 verwenden, über die Media3 API mit Ihrem Dienst kommunizieren können, sollte
<action android:name="androidx.media3.session.MediaLibraryService"/>zusammen mit der Plattformoption bereitgestellt werden.
So können Apps Ihren Dienst über PackageManager finden und über eine dieser Schnittstellen eine Verbindung zu einem MediaBrowser herstellen.
MediaLibrarySession verwenden
Die MediaLibraryService API erwartet, dass deine Mediathek in einem Baumformat strukturiert ist, mit einem einzelnen Stammknoten und untergeordneten Knoten, die abspielbar oder weiter durchsuchbar sein können.
Ein MediaLibrarySession erweitert die MediaSession API, um APIs zum Durchsuchen von Inhalten hinzuzufügen. Im Vergleich zum MediaSession-Callback werden beim MediaLibrarySession-Callback Methoden wie die folgenden hinzugefügt:
onGetLibraryRoot(), wenn ein Client die Stamm-MediaItemeines Inhaltsbaums anfordertonGetChildren(), wenn ein Client die untergeordneten Elemente einesMediaItemim Inhaltsbaum anfordertonGetSearchResult(), wenn ein Client Suchergebnisse aus dem Inhaltsbaum für eine bestimmte Abfrage anfordert
Relevante Callback-Methoden enthalten ein LibraryParams-Objekt mit zusätzlichen Signalen zum Typ des Inhaltsbaums, an dem eine Client-App interessiert ist.
Befehlsschaltflächen für Mediendateien
Eine Sitzungs-App kann Befehlsschaltflächen deklarieren, die von einem MediaItem in der MediaMetadata unterstützt werden. So können einer Mediendatei ein oder mehrere CommandButton-Einträge zugewiesen werden, die ein Controller anzeigen und verwenden kann, um den benutzerdefinierten Befehl für das Element auf bequeme Weise an die Sitzung zu senden.
Befehlsschaltflächen für die Einrichtung auf der Sitzungsseite
Beim Erstellen der Sitzung deklariert eine Sitzungs-App die Befehlsschaltflächen, die eine Sitzung als benutzerdefinierte Befehle verarbeiten kann:
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(), ) // 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();
Beim Erstellen einer Mediendatei kann eine Sitzungs-App eine Reihe von unterstützten Befehls-IDs hinzufügen, die auf Sitzungsbefehle von Befehlsschaltflächen verweisen, die beim Erstellen der Sitzung eingerichtet wurden:
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();
Wenn ein Controller oder Browser eine Verbindung herstellt oder eine andere Methode der Sitzung Callback aufruft, kann die Sitzungs-App die an den Callback übergebene ControllerInfo prüfen, um die maximale Anzahl von Befehlsschaltflächen zu ermitteln, die ein Controller oder Browser anzeigen kann. Der an eine Callback-Methode übergebene ControllerInfo stellt einen Getter bereit, mit dem Sie bequem auf diesen Wert zugreifen können. Standardmäßig ist der Wert auf 0 gesetzt, was bedeutet, dass der Browser oder Controller diese Funktion nicht unterstützt:
Kotlin
override fun onGetItem( session: MediaLibrarySession, browser: MediaSession.ControllerInfo, mediaId: String, ): ListenableFuture<LibraryResult<MediaItem>> { val settableFuture = SettableFuture.create<LibraryResult<MediaItem>>() val maxCommandsForMediaItems = browser.maxCommandsForMediaItems loadMediaItemAsync(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; }
Wenn eine benutzerdefinierte Aktion verarbeitet wird, die für ein Medienelement gesendet wurde, kann die Sitzungs-App die Medienelement-ID aus den Argumenten Bundle abrufen, die an onCustomCommand übergeben werden:
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); }
Befehlsschaltflächen als Browser oder Controller verwenden
Auf der MediaController-Seite kann eine App beim Erstellen des MediaController oder MediaBrowser die maximale Anzahl von Befehlsschaltflächen deklarieren, die sie für eine Mediendatei unterstützt:
Kotlin
val browserFuture = MediaBrowser.Builder(context, sessionToken).setMaxCommandsForMediaItems(3).buildAsync()
Java
ListenableFuture<MediaBrowser> browserFuture = new MediaBrowser.Builder(context, sessionToken).setMaxCommandsForMediaItems(3).buildAsync();
Wenn die Controller-App mit der Session verbunden ist, kann sie die Befehlsschaltflächen empfangen, die vom Medienelement unterstützt werden und für die der Controller den von der Session-App gewährten verfügbaren Befehl hat:
Kotlin
val commandButtonsForMediaItem = controller.getCommandButtonsForMediaItem(mediaItem)
Java
ImmutableList<CommandButton> commandButtonsForMediaItem = controller.getCommandButtonsForMediaItem(mediaItem);
Kotlin
val future = controller.sendCustomCommand( requireNotNull(addToPlaylistButton.sessionCommand), mediaItem, Bundle.EMPTY, )
Java
ListenableFuture<SessionResult> future = controller.sendCustomCommand( checkNotNull(addToPlaylistButton.sessionCommand), mediaItem, Bundle.EMPTY);