Il est souvent souhaitable de lire le contenu multimédia lorsqu'une application n'est pas au premier plan. Pour Exemple : un lecteur de musique continue de lire de la musique alors que l'utilisateur a verrouillé son appareil ou utilise une autre application. La bibliothèque Media3 fournit une série qui vous permettent de prendre en charge la lecture en arrière-plan.
Utiliser MediaSessionService
Pour activer la lecture en arrière-plan, vous devez contenir les éléments Player
et
MediaSession
dans un service distinct.
Cela permet à l'appareil de continuer à diffuser des contenus multimédias même lorsque votre appli n'est pas dans
au premier plan.
Lorsque vous hébergez un joueur dans un service, vous devez utiliser un MediaSessionService
.
Pour ce faire, créez une classe qui étend MediaSessionService
` et créez votre
et la session multimédia qu'elle contient.
L'utilisation de MediaSessionService
permet aux clients externes comme Google
l'Assistant, les commandes multimédias du système ou les appareils associés comme Wear OS afin de les découvrir
à votre service, vous y connecter et contrôler la lecture, le tout sans avoir
l'activité de l'interface utilisateur
de l'application. En fait, plusieurs applications clientes peuvent être connectées
vers le même MediaSessionService
en même temps, chaque application ayant sa propre
MediaController
Implémenter le cycle de vie du service
Vous devez implémenter trois méthodes de cycle de vie de votre service:
onCreate()
est appelé lorsque la première manette est sur le point de se connecter et que le service est instancié et démarré. C'est le meilleur endroit pour créerPlayer
etMediaSession
onTaskRemoved(Intent)
est appelé lorsque l'utilisateur ferme l'application de la tâches récentes. Si la lecture est en cours, l'application peut choisir de conserver le service qui s'exécute au premier plan. Si le lecteur est en pause, le service n'est pas dans le au premier plan et doit être arrêté.onDestroy()
est appelé lorsque le service est arrêté. Toutes les ressources y compris le lecteur et la session, doivent être libérés.
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(); } }
Au lieu de poursuivre la lecture en arrière-plan, une application peut arrêter le service dans tous les cas lorsque l'utilisateur ferme l'application:
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(); }
Accorder l'accès à la session multimédia
Ignorez la méthode onGetSession()
pour permettre à d'autres clients d'accéder à votre contenu multimédia.
qui a été créée lors de la création du service.
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; } }
Déclarer le service dans le fichier manifeste
Une application nécessite une autorisation pour exécuter un service de premier plan. Ajoutez le
l'autorisation FOREGROUND_SERVICE
pour le fichier manifeste. Si vous ciblez le niveau d'API 34 et
ci-dessus également FOREGROUND_SERVICE_MEDIA_PLAYBACK
:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
Vous devez également déclarer votre classe Service
dans le fichier manifeste avec un filtre d'intent.
sur MediaSessionService
.
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/>
</intent-filter>
</service>
Vous devez définir
foregroundServiceType
qui inclut mediaPlayback
lorsque votre application s'exécute sur un appareil Android
10 (niveau d'API 29) ou version ultérieure.
Contrôler la lecture avec un MediaController
Dans l'activité ou le fragment contenant l'interface utilisateur de votre lecteur, vous pouvez établir un lien
entre l'UI et votre session multimédia à l'aide d'un MediaController
. Votre UI utilise
le contrôleur multimédia pour envoyer des commandes de votre interface utilisateur au lecteur
session. Consultez le
Créer un MediaController
pour plus de détails sur la création et l'utilisation d'un MediaController
.
Gérer les commandes d'interface utilisateur
Le MediaSession
reçoit les commandes du contrôleur via son
MediaSession.Callback
L'initialisation d'un MediaSession
crée une
implémentation de MediaSession.Callback
, qui gère automatiquement toutes
commandes envoyées par un MediaController
à votre lecteur.
Notification
Un MediaSessionService
crée automatiquement un MediaNotification
pour vous qui
devrait fonctionner dans la plupart des cas. Par défaut, la notification publiée est un
Notification MediaStyle
qui s'actualise en permanence
depuis votre session multimédia et affiche les commandes de lecture. MediaNotification
connaît votre session et peut être utilisée pour contrôler la lecture de n'importe quelle autre application
qui sont connectés à la même session.
Par exemple, une application de streaming musical utilisant un MediaSessionService
créerait un
MediaNotification
qui affiche le titre, l'artiste et la pochette de l'album pour
l'élément multimédia en cours de lecture, parallèlement aux commandes de lecture en fonction de votre
Configuration MediaSession
.
Les métadonnées requises peuvent être fournies dans le support ou déclarées dans le cadre de élément multimédia comme dans l'extrait suivant:
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();
Les applications peuvent personnaliser les boutons de commande des commandes multimédias Android. En savoir plus sur la personnalisation des contenus multimédias Android de commande.
Personnalisation des notifications
Pour personnaliser la notification, créez un
MediaNotification.Provider
avec DefaultMediaNotificationProvider.Builder
ou en créant une implémentation
personnalisée de l'interface du fournisseur. Ajoutez votre
à votre MediaSessionService
avec
setMediaNotificationProvider
Reprise de la lecture
Les boutons multimédias sont des boutons physiques présents sur les appareils Android et les autres périphériques comme le bouton de lecture ou de pause d'un casque Bluetooth. Médias 3 gère pour vous les entrées des boutons multimédias lorsque le service est en cours d'exécution.
Déclarer le récepteur du bouton multimédia Media3
Media3 inclut une API permettant aux utilisateurs de reprendre
de lecture après l'arrêt d'une application et même après l'arrêt
redémarré. Par défaut, la reprise de lecture est désactivée. Cela signifie que l'utilisateur
ne peut pas reprendre la lecture lorsque votre service n'est pas en cours d'exécution. Pour l'activer, commencez par
déclarez l'élément MediaButtonReceiver
dans votre fichier manifeste:
<receiver android:name="androidx.media3.session.MediaButtonReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
Implémenter un rappel de reprise de lecture
Lorsque la reprise de la lecture est demandée par un appareil Bluetooth ou
Fonctionnalité de reprise de l'UI du système Android
la onPlaybackResumption()
est appelée.
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; }
Si vous avez enregistré d'autres paramètres tels que la vitesse de lecture, le mode de répétition ou
mode aléatoire, onPlaybackResumption()
permet de configurer le lecteur
avec ces paramètres avant que Media3 ne prépare le lecteur et démarre la lecture lorsque
le rappel est terminé.
Configuration avancée de la manette et rétrocompatibilité
Un scénario courant consiste à utiliser un MediaController
dans l'UI de l'application pour contrôler
la lecture et l'affichage de la playlist. Dans le même temps, la session est présentée
à des clients externes comme les commandes multimédias Android et l'Assistant sur mobile ou téléviseur,
Wear OS pour les montres et Android Auto dans les voitures. L'application de démonstration de la session Media3
est un exemple d'application qui met en œuvre un tel scénario.
Ces clients externes peuvent utiliser des API telles que MediaControllerCompat
de l'ancienne
Bibliothèque AndroidX ou android.media.session.MediaController
de l'application
d'infrastructure. Media3 est entièrement rétrocompatible avec l'ancienne bibliothèque et
assure l'interopérabilité avec l'API du framework Android.
Utiliser le contrôleur de notifications multimédias
Il est important de comprendre que ces anciens contrôleurs ou contrôleurs lisent
les mêmes valeurs que celles du framework PlaybackState.getActions()
et
PlaybackState.getCustomActions()
Pour déterminer les actions et les actions personnalisées
la session de framework, une application peut utiliser le contrôleur de notification multimédia.
et définir les commandes disponibles
et la mise en page personnalisée. Le service connecte le média
de notification à votre session, et celle-ci utilise
ConnectionResult
renvoyé par le onConnect()
de votre rappel pour configurer
et les actions personnalisées de la session du framework.
Dans un scénario réservé aux mobiles, une application peut fournir une implémentation
MediaSession.Callback.onConnect()
pour définir les commandes disponibles et
mise en page personnalisée spécifiquement pour la session du framework, comme suit:
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(); }
Autoriser Android Auto à envoyer des commandes personnalisées
Lorsque vous utilisez un MediaLibraryService
et pour prendre en charge Android Auto avec l'application mobile, la télécommande Android Auto
requiert les commandes disponibles appropriées, sinon Media3 refuserait
des commandes personnalisées entrantes de ce contrôleur:
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(); }
L'application de démonstration de la session module automobile, qui démontre la prise en charge d'Automotive OS nécessitant un APK distinct.