Oft ist es wünschenswert, Medien abzuspielen, während eine App nicht im Vordergrund ist. Ein Musikplayer spielt beispielsweise in der Regel weiter Musik ab, wenn der Nutzer sein Gerät gesperrt hat oder eine andere App verwendet. Die Media3-Bibliothek bietet eine Reihe von Schnittstellen, mit denen Sie die Hintergrundwiedergabe unterstützen können.
MediaSessionService verwenden
Um die Hintergrundwiedergabe zu aktivieren, sollten Sie die Player und
MediaSession in einem separaten Dienst unterbringen.
So kann das Gerät weiterhin Medien bereitstellen, auch wenn Ihre App nicht im Vordergrund ist.
MediaSessionService kann die Mediensitzung unabhängig von der Aktivität der App ausgeführt werden.Wenn Sie einen Player in einem Dienst hosten, sollten Sie MediaSessionService verwenden.
Erstellen Sie dazu eine Klasse, die MediaSessionService erweitert, und erstellen Sie darin Ihre Mediensitzung.
Mit MediaSessionService können externe Clients wie Google Assistant, die Mediensteuerung des Systems, Medientasten auf Peripheriegeräten oder Begleitgeräte wie Wear OS Ihren Dienst erkennen, eine Verbindung herstellen und die Wiedergabe steuern, ohne auf die UI-Aktivität Ihrer App zuzugreifen. Tatsächlich können mehrere Client-Apps gleichzeitig mit demselben MediaSessionService verbunden sein, wobei jede App einen eigenen MediaController hat.
Dienstlebenszyklus implementieren
Sie müssen zwei Lebenszyklusmethoden Ihres Dienstes implementieren:
onCreate()wird aufgerufen, wenn sich der erste Controller verbinden möchte und der Dienst instanziiert und gestartet wird. Hier können SiePlayerundMediaSessionerstellen.onDestroy()wird aufgerufen, wenn der Dienst beendet wird. Alle Ressourcen, einschließlich Player und Sitzung, müssen freigegeben werden.
Optional können Sie onTaskRemoved(Intent) überschreiben, um anzupassen, was passiert, wenn der Nutzer die App aus den letzten Aufgaben entfernt. Standardmäßig wird der Dienst weiter ausgeführt, wenn die Wiedergabe läuft, und andernfalls beendet.
Kotlin
class PlaybackService : MediaSessionService() { private var mediaSession: MediaSession? = null // Create your Player and MediaSession in the onCreate lifecycle event override fun onCreate() { super.onCreate() val player = ExoPlayer.Builder(this).build() mediaSession = MediaSession.Builder(this, player).build() } // Remember to release the player and media session in onDestroy override fun onDestroy() { mediaSession?.run { player.release() release() mediaSession = null } super.onDestroy() } override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? = mediaSession }
Java
class PlaybackService extends MediaSessionService { private MediaSession mediaSession = null; // Create your Player and MediaSession in the onCreate lifecycle event @Override public void onCreate() { super.onCreate(); ExoPlayer player = new ExoPlayer.Builder(this).build(); mediaSession = new MediaSession.Builder(this, player).build(); } // Remember to release the player and media session in onDestroy @Override public void onDestroy() { mediaSession.getPlayer().release(); mediaSession.release(); mediaSession = null; super.onDestroy(); } @Override public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) { return mediaSession; } }
Alternativ zur fortlaufenden Wiedergabe im Hintergrund können Sie den Dienst in jedem Fall beenden, wenn der Nutzer die App entfernt:
Kotlin
@OptIn(UnstableApi::class) override fun onTaskRemoved(rootIntent: Intent?) { pauseAllPlayersAndStopSelf() }
Java
@OptIn(markerClass = UnstableApi.class) @Override public void onTaskRemoved(@Nullable Intent rootIntent) { pauseAllPlayersAndStopSelf(); }
Für jede andere manuelle Implementierung von onTaskRemoved können Sie mit isPlaybackOngoing() prüfen, ob die Wiedergabe als fortlaufend betrachtet wird und der Dienst im Vordergrund gestartet wird.
Zugriff auf die Mediensitzung gewähren
Überschreiben Sie die Methode onGetSession(), um anderen Clients Zugriff auf Ihre Mediensitzung zu gewähren, die beim Erstellen des Dienstes erstellt wurde.
Kotlin
class PlaybackService : MediaSessionService() { // [...] lifecycle methods omitted override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? = mediaSession }
Java
class PlaybackService extends MediaSessionService { // [...] lifecycle methods omitted @Override public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) { return mediaSession; } }
Dienst im Manifest deklarieren
Für eine App sind die FOREGROUND_SERVICE und FOREGROUND_SERVICE_MEDIA_PLAYBACK
Berechtigungen erforderlich, um einen Dienst im Vordergrund für die Wiedergabe auszuführen:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
Sie müssen Ihre Service-Klasse auch im Manifest mit einem Intent-Filter
von MediaSessionService und einem foregroundServiceType deklarieren, der
mediaPlayback enthält.
<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>
Wiedergabe mit MediaController steuern
In der Aktivität oder dem Fragment, das die Player-UI enthält, können Sie mit MediaController eine Verknüpfung zwischen der UI und Ihrer Mediensitzung herstellen. Über die Mediensteuerung werden Befehle von der UI an den Player in der Sitzung gesendet. Weitere Informationen zum Erstellen und Verwenden von MediaController finden Sie im
Leitfaden MediaController.
MediaController-Befehle verarbeiten
Der MediaSession empfängt Befehle vom Controller über den
MediaSession.Callback. Beim Initialisieren von MediaSession wird eine Standard
implementierung von MediaSession.Callback erstellt, die alle
Befehle verarbeitet, die ein MediaController an Ihren Player sendet.
Benachrichtigung
Ein MediaSessionService erstellt automatisch eine MediaNotification für Sie, die in den meisten Fällen funktionieren sollte. Standardmäßig ist die veröffentlichte Benachrichtigung eine
MediaStyle Benachrichtigung, die mit den neuesten
Informationen aus Ihrer Mediensitzung aktualisiert wird und Wiedergabesteuerelemente anzeigt. MediaNotification kennt Ihre Sitzung und kann verwendet werden, um die Wiedergabe für alle anderen Apps zu steuern, die mit derselben Sitzung verbunden sind.
Eine Musikstreaming-App, die MediaSessionService verwendet, würde beispielsweise eine
MediaNotification erstellen, in der der Titel, der Künstler und das Albumcover des
aktuellen Medienelements zusammen mit Wiedergabesteuerelementen basierend auf Ihrer
MediaSession Konfiguration angezeigt werden.
Die erforderlichen Metadaten können in den Medien angegeben oder als Teil des Medienelements deklariert werden, wie im folgenden Snippet:
Kotlin
val mediaItem = MediaItem.Builder() .setMediaId("media-1") .setUri(mediaUri) .setMediaMetadata( MediaMetadata.Builder() .setArtist("David Bowie") .setTitle("Heroes") .setArtworkUri(artworkUri) .build() ) .build() mediaController.setMediaItem(mediaItem) mediaController.prepare() mediaController.play()
Java
MediaItem mediaItem = new MediaItem.Builder() .setMediaId("media-1") .setUri(mediaUri) .setMediaMetadata( new MediaMetadata.Builder() .setArtist("David Bowie") .setTitle("Heroes") .setArtworkUri(artworkUri) .build()) .build(); mediaController.setMediaItem(mediaItem); mediaController.prepare(); mediaController.play();
Lebenszyklus der Benachrichtigung
Die Benachrichtigung wird erstellt, sobald der Player MediaItem-Instanzen in seiner Playlist hat.
Alle Benachrichtigungsaktualisierungen erfolgen automatisch basierend auf dem Status von Player und MediaSession.
Die Benachrichtigung kann nicht entfernt werden, während der Dienst im Vordergrund ausgeführt wird. Wenn Sie die Benachrichtigung sofort entfernen möchten, müssen Sie Player.release() aufrufen oder die Playlist mit Player.clearMediaItems() löschen.
Wenn der Player länger als 10 Minuten pausiert, beendet oder fehlgeschlagen ist, ohne dass weitere Nutzerinteraktionen erfolgen, wird der Dienst automatisch aus dem Status des Dienstes im Vordergrund entfernt, damit er vom System beendet werden kann. Sie können die Wiedergabe fortsetzen, damit ein Nutzer den Dienstlebenszyklus neu starten und die Wiedergabe zu einem späteren Zeitpunkt fortsetzen kann.
Benachrichtigung anpassen
Die Metadaten zum aktuell wiedergegebenen Element können angepasst werden, indem Sie MediaItem.MediaMetadata ändern. Wenn Sie die Metadaten eines vorhandenen Elements aktualisieren möchten, können Sie Player.replaceMediaItem verwenden, um die Metadaten zu aktualisieren, ohne die Wiedergabe zu unterbrechen.
Sie können auch einige der in der Benachrichtigung angezeigten Schaltflächen anpassen, indem Sie benutzerdefinierte Einstellungen für Medientasten für die Android-Mediensteuerung festlegen. Weitere Informationen zum Anpassen der Android-Mediensteuerung.
Wenn Sie die Benachrichtigung weiter anpassen möchten, erstellen Sie ein
MediaNotification.Provider
mit DefaultMediaNotificationProvider.Builder
oder durch Erstellen einer benutzerdefinierten Implementierung der Provider-Schnittstelle. Fügen Sie Ihren
Provider zu Ihrem MediaSessionService mit
setMediaNotificationProvider hinzu.
Wiedergabe fortsetzen
Nachdem MediaSessionService beendet wurde und auch nach einem Neustart des Geräts ist es möglich, die Wiedergabe fortzusetzen, damit Nutzer den Dienst neu starten und die Wiedergabe an der Stelle fortsetzen können, an der sie unterbrochen wurde. Standardmäßig ist die Wiedergabe fortsetzen deaktiviert. Das bedeutet, dass der Nutzer die Wiedergabe nicht fortsetzen kann, wenn Ihr Dienst nicht ausgeführt wird. Wenn Sie diese Funktion aktivieren möchten, müssen Sie einen Empfänger für Medientasten deklarieren und die Methode onPlaybackResumption implementieren.
Empfänger für Media3-Medientasten deklarieren
Deklarieren Sie zuerst den MediaButtonReceiver in Ihrem Manifest:
<receiver android:name="androidx.media3.session.MediaButtonReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
Callback für die Wiedergabefortsetzung implementieren
Wenn die Wiedergabefortsetzung entweder von einem Bluetooth-Gerät oder über die
Funktion zur Fortsetzung der Android-System-UI angefordert wird,
wird die onPlaybackResumption() Callback-Methode
aufgerufen.
Kotlin
override fun onPlaybackResumption( mediaSession: MediaSession, controller: MediaSession.ControllerInfo, isForPlayback: Boolean, ): ListenableFuture<MediaSession.MediaItemsWithStartPosition> { val settableFuture = SettableFuture.create<MediaSession.MediaItemsWithStartPosition>() settableFuture.addListener( { // Your app is responsible for storing the playlist, metadata (like title // and artwork) of the current item and the start position to use here. val resumptionPlaylist = restorePlaylist() settableFuture.set(resumptionPlaylist) }, MoreExecutors.directExecutor(), ) return settableFuture }
Java
@Override public ListenableFuture<MediaItemsWithStartPosition> onPlaybackResumption( MediaSession mediaSession, ControllerInfo controller, boolean isForPlayback) { SettableFuture<MediaItemsWithStartPosition> settableFuture = SettableFuture.create(); settableFuture.addListener( () -> { // Your app is responsible for storing the playlist, metadata (like title // and artwork) of the current item and the start position to use here. MediaItemsWithStartPosition resumptionPlaylist = restorePlaylist(); settableFuture.set(resumptionPlaylist); }, MoreExecutors.directExecutor()); return settableFuture; }
Wenn Sie andere Parameter wie Wiedergabegeschwindigkeit, Wiederholungsmodus oder Zufallsmodus gespeichert haben, können Sie onPlaybackResumption() verwenden, um den Player mit diesen Parametern zu konfigurieren, bevor Media3 den Player vorbereitet und die Wiedergabe startet, wenn der Callback abgeschlossen ist.
Diese Methode wird während des Starts aufgerufen, um die Benachrichtigung zur Fortsetzung der Android-System-UI nach einem Neustart des Geräts zu erstellen, wobei isForPlayback auf false gesetzt ist. Für eine Rich-Benachrichtigung empfiehlt es sich, MediaMetadata-Felder wie title und artworkData oder artworkUri des aktuellen Elements mit lokal verfügbaren Werten zu füllen, da der Netzwerkzugriff möglicherweise noch nicht verfügbar ist. Sie können auch MediaConstants.EXTRAS_KEY_COMPLETION_STATUS und MediaConstants.EXTRAS_KEY_COMPLETION_PERCENTAGE zu MediaMetadata.extras hinzufügen, um die Wiedergabeposition für die Fortsetzung anzugeben.
Erweiterte Controller-Konfiguration und Abwärtskompatibilität
Ein häufiges Szenario ist die Verwendung von MediaController in der App-UI, um die Wiedergabe zu steuern und die Playlist anzuzeigen. Gleichzeitig wird die Sitzung für externe Clients wie die Android-Mediensteuerung und Assistant auf Mobilgeräten oder Fernsehern, Wear OS für Smartwatches und Android Auto in Autos verfügbar gemacht. Die Media3-Sitzungsdemo-App
ist ein Beispiel für eine App, die ein solches Szenario implementiert.
Diese externen Clients können APIs wie MediaControllerCompat der älteren AndroidX-Bibliothek oder android.media.session.MediaController der Android-Plattform verwenden. Media3 ist vollständig abwärtskompatibel mit der älteren Bibliothek und bietet Interoperabilität mit der Android-Plattform-API.
Vertrauenswürdige Controller identifizieren
Jede App kann versuchen, eine Verbindung zu Ihrer Mediensitzung oder ‑bibliothek herzustellen. Wenn Sie den Zugriff auf Systemcontroller, Controller mit Berechtigung zur Steuerung von Medieninhalten und Ihre eigene App beschränken möchten, können Sie ControllerInfo.isTrusted() für eine grundlegende Zugriffsprüfung verwenden. Alternativ können Sie genauere Controller wie den Controller für Medienbenachrichtigungen oder Android Auto-Controller identifizieren, wie in den folgenden Abschnitten beschrieben.
Controller für Medienbenachrichtigungen verwenden
Es ist wichtig zu wissen, dass diese älteren und Plattform-Controller denselben Status haben und die Sichtbarkeit nicht nach Controller angepasst werden kann (z. B. die verfügbaren PlaybackState.getActions() und PlaybackState.getCustomActions()). Sie können den Controller für Medienbenachrichtigungen verwenden, um den in der Plattform-Mediensitzung festgelegten Status für die Kompatibilität mit diesen älteren und Plattform-Controllern zu konfigurieren.
Eine App kann beispielsweise eine Implementierung von MediaSession.Callback.onConnect() bereitstellen, um verfügbare Befehle und Einstellungen für Medientasten speziell für die Plattformsitzung festzulegen:
Kotlin
override fun onConnectAsync( session: MediaSession, controller: MediaSession.ControllerInfo, ): ListenableFuture<ConnectionResult> { if (session.isMediaNotificationController(controller)) { val playerCommands = ConnectionResult.DEFAULT_PLAYER_COMMANDS.buildUpon() .remove(COMMAND_SEEK_TO_PREVIOUS) .remove(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM) .remove(COMMAND_SEEK_TO_NEXT) .remove(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM) .build() // Custom button preferences and commands to configure the platform session. return immediateFuture( AcceptedResultBuilder(session) .setMediaButtonPreferences(listOf(seekBackButton, seekForwardButton)) .setAvailablePlayerCommands(playerCommands) .build() ) } // Default commands with default button preferences for all other controllers. return immediateFuture(AcceptedResultBuilder(session).build()) }
Java
@Override public ListenableFuture<ConnectionResult> onConnectAsync( MediaSession session, MediaSession.ControllerInfo controller) { if (session.isMediaNotificationController(controller)) { Player.Commands playerCommands = ConnectionResult.DEFAULT_PLAYER_COMMANDS .buildUpon() .remove(COMMAND_SEEK_TO_PREVIOUS) .remove(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM) .remove(COMMAND_SEEK_TO_NEXT) .remove(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM) .build(); // Custom button preferences and commands to configure the platform session. return immediateFuture( new AcceptedResultBuilder(session) .setMediaButtonPreferences(ImmutableList.of(seekBackButton, seekForwardButton)) .setAvailablePlayerCommands(playerCommands) .build()); } // Default commands with default button preferences for all other controllers. return immediateFuture(new AcceptedResultBuilder(session).build()); }
Android Auto zum Senden benutzerdefinierter Befehle autorisieren
Wenn Sie MediaLibraryService
verwenden und Android Auto mit der mobilen App unterstützen möchten, benötigt der Android Auto-Controller
entsprechende verfügbare Befehle. Andernfalls lehnt Media3
eingehende benutzerdefinierte Befehle von diesem Controller ab:
Kotlin
override fun onConnectAsync( session: MediaSession, controller: MediaSession.ControllerInfo, ): ListenableFuture<ConnectionResult> { val sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon().add(customCommand).build() if (session.isMediaNotificationController(controller)) { // ... See above. } else if (session.isAutoCompanionController(controller)) { // Available commands to accept incoming custom commands from Auto. return immediateFuture( AcceptedResultBuilder(session).setAvailableSessionCommands(sessionCommands).build() ) } // Default commands for all other controllers. return immediateFuture(AcceptedResultBuilder(session).build()) }
Java
@Override public ListenableFuture<ConnectionResult> onConnectAsync( MediaSession session, MediaSession.ControllerInfo controller) { SessionCommands sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon().add(customCommand).build(); if (session.isMediaNotificationController(controller)) { // ... See above. } else if (session.isAutoCompanionController(controller)) { // Available commands to accept incoming custom commands from Auto. return immediateFuture( new AcceptedResultBuilder(session) .setAvailableSessionCommands(sessionCommands) .build()); } // Default commands for all other controllers. return immediateFuture(new AcceptedResultBuilder(session).build()); }
Die Sitzungsdemo-App hat ein Automobilmodul, das die Unterstützung für Automotive OS demonstriert, für das eine separate APK erforderlich ist.