Es ist oft wünschenswert, Medien abzuspielen, während eine App nicht im Vordergrund ausgeführt wird. Beispielsweise wird bei einem Musikplayer in der Regel Musik abgespielt, 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, musst du Player
und
MediaSession
innerhalb eines separaten Service.
So kann das Gerät weiterhin Medien bereitstellen, auch wenn sich deine App nicht in
im Vordergrund.
Wenn du einen Player in einem Dienst hostest, solltest du einen MediaSessionService
verwenden.
Erstelle dazu eine Klasse, die MediaSessionService
erweitert, und erstelle darin deine Mediensitzung.
Mit MediaSessionService
können externe Clients wie Google Assistant, Systemmediensteuerungen oder Companion-Geräte wie Wear OS deinen Dienst finden, eine Verbindung dazu herstellen und die Wiedergabe steuern, ohne auf die UI-Aktivitäten deiner App zuzugreifen. Es können sogar mehrere Client-Apps
in dieselbe MediaSessionService
, jede App hat eine eigene
MediaController
.
Dienstlebenszyklus implementieren
Sie müssen drei Lebenszyklusmethoden für Ihren Dienst implementieren:
onCreate()
wird aufgerufen, wenn der erste Controller eine Verbindung herstellen soll und der instanziiert und gestartet wird. Es ist der beste Ort, umPlayer
undMediaSession
zu entwickeln.onTaskRemoved(Intent)
wird aufgerufen, wenn der Nutzer die App aus der die letzten Aufgaben. Wenn die Wiedergabe aktiv ist, kann die App den Dienst im Vordergrund laufen lassen. Wenn der Player pausiert ist, ist der Dienst nicht im Vordergrund und muss beendet werden.onDestroy()
wird aufgerufen, wenn der Dienst beendet wird. Alle Ressourcen, einschließlich Player und Sitzung, müssen freigegeben werden.
Kotlin
class PlaybackService : MediaSessionService() { private var mediaSession: MediaSession? = null // Create your player and media session in the onCreate lifecycle event override fun onCreate() { super.onCreate() val player = ExoPlayer.Builder(this).build() mediaSession = MediaSession.Builder(this, player).build() } // The user dismissed the app from the recent tasks override fun onTaskRemoved(rootIntent: Intent?) { val player = mediaSession?.player!! if (!player.playWhenReady || player.mediaItemCount == 0 || player.playbackState == Player.STATE_ENDED) { // Stop the service if not playing, continue playing in the background // otherwise. stopSelf() } } // Remember to release the player and media session in onDestroy override fun onDestroy() { mediaSession?.run { player.release() release() mediaSession = null } super.onDestroy() } }
Java
public 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(); } // The user dismissed the app from the recent tasks @Override public void onTaskRemoved(@Nullable Intent rootIntent) { Player player = mediaSession.getPlayer(); if (!player.getPlayWhenReady() || player.getMediaItemCount() == 0 || player.getPlaybackState() == Player.STATE_ENDED) { // Stop the service if not playing, continue playing in the background // otherwise. stopSelf(); } } // Remember to release the player and media session in onDestroy @Override public void onDestroy() { mediaSession.getPlayer().release(); mediaSession.release(); mediaSession = null; super.onDestroy(); } }
Anstatt die Wiedergabe im Hintergrund fortzusetzen, kann eine App den Dienst in jedem Fall beenden, wenn der Nutzer die App schließt:
Kotlin
override fun onTaskRemoved(rootIntent: Intent?) { val player = mediaSession.player if (player.playWhenReady) { // Make sure the service is not in foreground. player.pause() } stopSelf() }
Java
@Override public void onTaskRemoved(@Nullable Intent rootIntent) { Player player = mediaSession.getPlayer(); if (player.getPlayWhenReady()) { // Make sure the service is not in foreground. player.pause(); } stopSelf(); }
Zugriff auf die Mediensitzung gewähren
Überschreiben Sie die Methode onGetSession()
, um anderen Clients Zugriff auf Ihre Medien zu gewähren
Sitzung, die beim Erstellen des Dienstes erstellt wurde.
Kotlin
class PlaybackService : MediaSessionService() { private var mediaSession: MediaSession? = null // [...] lifecycle methods omitted override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? = mediaSession }
Java
public class PlaybackService extends MediaSessionService { private MediaSession mediaSession = null; // [...] lifecycle methods omitted @Override public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) { return mediaSession; } }
Dienst im Manifest deklarieren
Eine App benötigt eine Berechtigung, um einen Dienst im Vordergrund auszuführen. Fügen Sie den
FOREGROUND_SERVICE
für das Manifest und wenn Sie API 34 und
über FOREGROUND_SERVICE_MEDIA_PLAYBACK
:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
Außerdem müssen Sie Ihre Service
-Klasse im Manifest mit einem Intent-Filter von MediaSessionService
deklarieren.
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/>
</intent-filter>
</service>
Sie müssen eine foregroundServiceType
definieren, die mediaPlayback
enthält, wenn Ihre App auf einem Gerät mit Android 10 (API-Level 29) oder höher ausgeführt wird.
Wiedergabe mit MediaController
steuern
In der Aktivität oder dem Fragment, die deine Player-Benutzeroberfläche enthält, kannst du einen Link
zwischen der UI und der Mediensitzung mit einem MediaController
. Über den Mediencontroller sendet deine Benutzeroberfläche Befehle von der Benutzeroberfläche an den Player innerhalb der Sitzung. Weitere Informationen zum Erstellen und Verwenden von MediaController
finden Sie im Leitfaden MediaController
erstellen.
UI-Befehle verarbeiten
MediaSession
empfängt Befehle vom Controller über
MediaSession.Callback
. Wenn du eine MediaSession
initialisierst, wird eine Standardimplementierung von MediaSession.Callback
erstellt, die automatisch alle Befehle verarbeitet, die ein MediaController
an deinen Player sendet.
Benachrichtigung
Mit einer MediaSessionService
wird automatisch eine MediaNotification
für Sie erstellt, die in den meisten Fällen funktionieren sollte. Standardmäßig ist die veröffentlichte Benachrichtigung
MediaStyle
Benachrichtigung
die immer auf dem neuesten Stand ist,
aus Ihrer Mediensitzung und zeigt die Wiedergabesteuerung an. Das MediaNotification
erkennt Ihre Sitzung und kann zur Steuerung der Wiedergabe für alle anderen Apps verwendet werden.
die mit derselben Sitzung verbunden sind.
Beispiel: Eine Musikstreaming-App mit MediaSessionService
würde einen
MediaNotification
, die den Titel, den Künstler und das Albumcover des
das aktuell abgespielte Medienelement zusammen mit der Wiedergabesteuerung basierend auf Ihrer
MediaSession
-Konfiguration.
Die erforderlichen Metadaten können in den Medien bereitgestellt oder wie im folgenden Snippet als Teil des Medienelements deklariert werden:
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();
Apps können die Befehlsschaltflächen der Android-Mediensteuerung anpassen. Weitere Informationen zum Anpassen der Mediensteuerung unter Android
Benachrichtigungen anpassen
Wenn Sie die Benachrichtigung anpassen möchten, erstellen Sie eine MediaNotification.Provider
mit DefaultMediaNotificationProvider.Builder
oder eine benutzerdefinierte Implementierung der Anbieteroberfläche. Fügen Sie Ihr
auf MediaSessionService
mit
setMediaNotificationProvider
Wiedergabe fortsetzen
Medientasten sind Hardwaretasten auf Android-Geräten und anderen Peripheriegeräten, z. B. die Wiedergabe- oder Pausetaste auf einem Bluetooth-Headset. Media3 verarbeitet die Eingaben der Medienschaltflächen für Sie, wenn der Dienst ausgeführt wird.
Empfänger der Media3-Medientaste deklarieren
Media3 enthält eine API, mit der Nutzer die Wiedergabe fortsetzen können, nachdem eine App beendet wurde und sogar nach einem Neustart des Geräts. Standardmäßig ist die Wiederaufnahme der Wiedergabe deaktiviert. Das bedeutet, dass die Nutzenden
können die Wiedergabe nicht fortsetzen, wenn der Dienst nicht ausgeführt wird. Wenn Sie die Funktion aktivieren möchten, deklarieren Sie zuerst die 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 Wiederaufnahme der Wiedergabe implementieren
Wenn die Fortsetzung der Wiedergabe von einem Bluetooth-Gerät oder vom
Wiederaufnahmefunktion der Android-System-UI
onPlaybackResumption()
Callback-Methode aufgerufen.
Kotlin
override fun onPlaybackResumption( mediaSession: MediaSession, controller: ControllerInfo ): ListenableFuture<MediaItemsWithStartPosition> { val settable = SettableFuture.create<MediaItemsWithStartPosition>() scope.launch { // Your app is responsible for storing the playlist and the start position // to use here val resumptionPlaylist = restorePlaylist() settable.set(resumptionPlaylist) } return settable }
Java
@Override public ListenableFuture<MediaItemsWithStartPosition> onPlaybackResumption( MediaSession mediaSession, ControllerInfo controller ) { SettableFuture<MediaItemsWithStartPosition> settableFuture = SettableFuture.create(); settableFuture.addListener(() -> { // Your app is responsible for storing the playlist and the start position // to use here MediaItemsWithStartPosition resumptionPlaylist = restorePlaylist(); settableFuture.set(resumptionPlaylist); }, MoreExecutors.directExecutor()); return settableFuture; }
Wenn du andere Parameter wie Wiedergabegeschwindigkeit, Wiederholungsmodus oder Zufallsmix gespeichert hast, kannst du den Player mit diesen Parametern in onPlaybackResumption()
konfigurieren, bevor Media3 den Player vorbereitet und die Wiedergabe startet, sobald der Rückruf abgeschlossen ist.
Erweiterte Controllerkonfiguration und Abwärtskompatibilität
Ein häufiges Szenario ist die Verwendung einer MediaController
in der App-UI zur Steuerung.
Wiedergabe und Wiedergabe der Playlist. Gleichzeitig wird die Sitzung
wie die Mediensteuerung von Android und Assistant auf Mobilgeräten oder Fernsehern,
Wear OS für Smartwatches und Android Auto in Autos Die Demo-App für die Media3-Sitzung
ist ein Beispiel für eine App, die ein solches Szenario implementiert.
Diese externen Clients verwenden möglicherweise APIs wie MediaControllerCompat
der Legacy-Version
AndroidX-Bibliothek oder android.media.session.MediaController
des Android-
Framework. Media3 ist vollständig abwärtskompatibel mit der alten Bibliothek und
ermöglicht Interoperabilität mit der Android Framework API.
Controller für Medienbenachrichtigungen verwenden
Wichtig ist, dass Legacy- oder Framework-Controller die
dieselben Werte aus dem Framework PlaybackState.getActions()
und
PlaybackState.getCustomActions()
. Um Aktionen und benutzerdefinierte Aktionen der Framework-Sitzung zu bestimmen, kann eine App den Media Notification Controller verwenden und die verfügbaren Befehle und das benutzerdefinierte Layout festlegen. Der Dienst verbindet den Media-Benachrichtigungscontroller mit deiner Sitzung. Die Sitzung verwendet den von onConnect()
deines Callbacks zurückgegebenen Wert ConnectionResult
, um Aktionen und benutzerdefinierte Aktionen der Framework-Sitzung zu konfigurieren.
Bei einem reinen Mobilgeräteszenario kann eine App eine Implementierung von MediaSession.Callback.onConnect()
bereitstellen, um verfügbare Befehle und ein benutzerdefiniertes Layout speziell für die Framework-Sitzung festzulegen. Dazu ist Folgendes erforderlich:
Kotlin
override fun onConnect( session: MediaSession, controller: MediaSession.ControllerInfo ): ConnectionResult { if (session.isMediaNotificationController(controller)) { val sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() .add(customCommandSeekBackward) .add(customCommandSeekForward) .build() 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 layout and available commands to configure the legacy/framework session. return AcceptedResultBuilder(session) .setCustomLayout( ImmutableList.of( createSeekBackwardButton(customCommandSeekBackward), createSeekForwardButton(customCommandSeekForward)) ) .setAvailablePlayerCommands(playerCommands) .setAvailableSessionCommands(sessionCommands) .build() } // Default commands with default custom layout for all other controllers. return AcceptedResultBuilder(session).build() }
Java
@Override public ConnectionResult onConnect( MediaSession session, MediaSession.ControllerInfo controller) { if (session.isMediaNotificationController(controller)) { SessionCommands sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS .buildUpon() .add(customCommandSeekBackward) .add(customCommandSeekForward) .build(); 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 layout and available commands to configure the legacy/framework session. return new AcceptedResultBuilder(session) .setCustomLayout( ImmutableList.of( createSeekBackwardButton(customCommandSeekBackward), createSeekForwardButton(customCommandSeekForward))) .setAvailablePlayerCommands(playerCommands) .setAvailableSessionCommands(sessionCommands) .build(); } // Default commands without default custom layout for all other controllers. return new AcceptedResultBuilder(session).build(); }
Android Auto zum Senden benutzerdefinierter Befehle autorisieren
Wenn du einen MediaLibraryService
verwendest und Android Auto mit der mobilen App unterstützen möchtest, benötigt der Android Auto-Controller entsprechende verfügbare Befehle. Andernfalls lehnt Media3 eingehende benutzerdefinierte Befehle von diesem Controller ab:
Kotlin
override fun onConnect( session: MediaSession, controller: MediaSession.ControllerInfo ): ConnectionResult { val sessionCommands = ConnectionResult.DEFAULT_SESSION_AND_LIBRARY_COMMANDS.buildUpon() .add(customCommandSeekBackward) .add(customCommandSeekForward) .build() if (session.isMediaNotificationController(controller)) { // [...] See above. } else if (session.isAutoCompanionController(controller)) { // Available session commands to accept incoming custom commands from Auto. return AcceptedResultBuilder(session) .setAvailableSessionCommands(sessionCommands) .build() } // Default commands with default custom layout for all other controllers. return AcceptedResultBuilder(session).build() }
Java
@Override public ConnectionResult onConnect( MediaSession session, MediaSession.ControllerInfo controller) { SessionCommands sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS .buildUpon() .add(customCommandSeekBackward) .add(customCommandSeekForward) .build(); if (session.isMediaNotificationController(controller)) { // [...] See above. } else if (session.isAutoCompanionController(controller)) { // Available commands to accept incoming custom commands from Auto. return new AcceptedResultBuilder(session) .setAvailableSessionCommands(sessionCommands) .build(); } // Default commands without default custom layout for all other controllers. return new AcceptedResultBuilder(session).build(); }
Die Sitzungs-Demo-App hat eine Automobilmodul , der die Unterstützung von Automotive OS zeigt, für die ein separates APK erforderlich ist.