Es ist oft wünschenswert, Medien abzuspielen, während eine App nicht im Vordergrund ausgeführt wird. Für Beispiel: Ein Musikplayer spielt Musik in der Regel weiter ab, wenn der Nutzer oder eine andere App verwendet. Die Media3-Bibliothek bietet Oberflächen, die die Hintergrundwiedergabe unterstützen.
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.
![Der MediaSessionService ermöglicht die separate Ausführung der Mediensitzung.
aus den Aktivitäten der App](https://developer.android.google.cn/static/guide/topics/media/images/mediasessionservice.png?authuser=0000&hl=de)
MediaSessionService
ermöglicht Medien,
Sitzung getrennt von der App-Aktivität ausgeführt werdenWenn du einen Player innerhalb eines Dienstes hostest, solltest du einen MediaSessionService
verwenden.
Erstellen Sie dazu eine Klasse, die MediaSessionService
erweitert, und erstellen Sie Ihr
Mediensitzung darin.
Mit MediaSessionService
können externe Kunden wie Google
Assistant, die Mediensteuerung des Systems oder Companion-Geräte wie Wear OS,
Ihren Dienst zu verwalten, eine Verbindung zu ihm herzustellen und die Wiedergabe zu steuern, ohne auf Ihre
die UI-Aktivitäten der App überprüfen. Es können sogar mehrere Client-Apps
in dieselbe MediaSessionService
, jede App hat eine eigene
MediaController
.
Dienstlebenszyklus implementieren
Sie müssen drei Lebenszyklusmethoden Ihres Dienstes implementieren:
onCreate()
wird aufgerufen, wenn der erste Controller eine Verbindung herstellen soll und der instanziiert und gestartet wird. Der beste Ort, umPlayer
undMediaSession
.onTaskRemoved(Intent)
wird aufgerufen, wenn der Nutzer die App aus der die letzten Aufgaben. Wenn die Wiedergabe läuft, kann die App den Dienst behalten im Vordergrund ausgeführt werden. Wenn der Player pausiert ist, befindet sich der Dienst nicht im 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 laufen zu lassen, kann eine App Auf jeden 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 die Berechtigung zum Ausführen eines Dienstes im Vordergrund. 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 die Klasse Service
im Manifest mit einem Intent-Filter deklarieren
von MediaSessionService
.
<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
einschließlich mediaPlayback
, wenn deine App auf einem Gerät mit Android ausgeführt wird
10 (API-Level 29) und höher.
Wiedergabe mit einem 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
. Ihre UI verwendet
Medien-Controller, um Befehle von Ihrer Benutzeroberfläche an den Player innerhalb der
Sitzung. Weitere Informationen finden Sie in der
MediaController
erstellen
Leitfaden zum Erstellen und Verwenden von MediaController
.
UI-Befehle verarbeiten
MediaSession
empfängt Befehle vom Controller über
MediaSession.Callback
. Beim Initialisieren eines MediaSession
wird ein Standardwert erstellt
Implementierung von MediaSession.Callback
, die automatisch alle
Befehle, die MediaController
an deinen Player sendet.
Benachrichtigung
Ein MediaSessionService
erstellt automatisch eine MediaNotification
für Sie, die
sollte in den meisten Fällen funktionieren. 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 als Teil des Medienelement wie im folgenden Snippet anzuzeigen:
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 von Android Media Controls anpassen. Weitere Informationen zur Anpassung von Android Media .
Benachrichtigungsanpassung
Wenn du die Benachrichtigung anpassen möchtest, erstelle ein
MediaNotification.Provider
mit DefaultMediaNotificationProvider.Builder
oder durch Erstellen einer benutzerdefinierten Implementierung der Anbieterschnittstelle. Fügen Sie Ihr
auf MediaSessionService
mit
setMediaNotificationProvider
Wiedergabe fortsetzen
Medienschaltflächen sind Hardwaretasten auf Android-Geräten und anderen Peripheriegeräten Geräte verwenden, z. B. die Wiedergabe- oder Pausetaste an einem Bluetooth-Headset. Medien 3 die Eingaben der Medienschaltfläche für Sie übernehmen, wenn der Dienst ausgeführt wird.
Empfänger der Media3-Medientaste deklarieren
Media3 enthält eine API, mit der Nutzer
nachdem eine App beendet wurde oder nachdem das Gerät
neu gestartet. Die Wiederaufnahme der Wiedergabe ist standardmäßig deaktiviert. Das bedeutet, dass die Nutzenden
können die Wiedergabe nicht fortsetzen, wenn der Dienst nicht ausgeführt wird. Wenn Sie sich anmelden möchten,
So deklarieren Sie das 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 Wiederaufnahme 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 Sie andere Parameter wie die Wiedergabegeschwindigkeit, den Wiederholungsmodus oder
Zufallsmix aktiviert ist, eignet sich onPlaybackResumption()
gut, um den Player
mit diesen Parametern ein, bevor Media3 den Player vorbereitet und die Wiedergabe startet,
der Callback abgeschlossen wird.
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, in der ein solches Szenario implementiert ist.
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.
Media Notification Controller verwenden
Wichtig ist, dass Legacy- oder Framework-Controller die
dieselben Werte aus dem Framework PlaybackState.getActions()
und
PlaybackState.getCustomActions()
. Um Aktionen und benutzerdefinierte Aktionen von
Framework-Sitzung kann eine App den Media Notification Controller verwenden.
und die verfügbaren Befehle und das
benutzerdefinierte Layout festlegen. Der Dienst verbindet die Medien,
Benachrichtigungs-Controller zu Ihrer Sitzung hinzu und diese verwendet die
ConnectionResult
, die zum Konfigurieren vom onConnect()
deines Callbacks zurückgegeben werden
Aktionen und benutzerdefinierten Aktionen der Framework-Sitzung.
Bei einem rein mobilen Szenario kann eine App die Implementierung einer
MediaSession.Callback.onConnect()
zum Festlegen der verfügbaren Befehle und
ein benutzerdefiniertes Layout speziell für die Framework-Sitzung:
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 autorisieren, benutzerdefinierte Befehle zu senden
Bei Verwendung eines MediaLibraryService
und zur Unterstützung von Android Auto mit der mobilen App den Android Auto-Controller
erfordert die entsprechenden verfügbaren Befehle. Andernfalls würde Media3 die
von diesem Controller empfangene benutzerdefinierte Befehle:
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 , in dem die Unterstützung von Automotive OS demonstriert wird, für die ein separates APK erforderlich ist.