Spesso è opportuno riprodurre contenuti multimediali quando un'app non è in primo piano. Ad esempio, un media player in genere continua a riprodurre musica quando l'utente ha bloccato il dispositivo o sta utilizzando un'altra app. La libreria Media3 fornisce una serie di interfacce che ti consentono di supportare la riproduzione in background.
Utilizzare un MediaSessionService
Per attivare la riproduzione in background, devi includere Player
e
MediaSession
in un servizio separato.
In questo modo, il dispositivo può continuare a riprodurre contenuti multimediali anche quando l'app non è in primo piano.

MediaSessionService
consente alla sessione multimediale di essere eseguita separatamente dall'attività dell'appQuando ospiti un player all'interno di un servizio, devi utilizzare un MediaSessionService
.
Per farlo, crea una classe che estenda MediaSessionService
e crea la sessione media al suo interno.
L'utilizzo di MediaSessionService
consente ai client esterni come l'Assistente Google, i controlli multimediali di sistema, i pulsanti multimediali sui dispositivi periferici o i dispositivi companion come Wear OS di rilevare il tuo servizio, connettersi e controllare la riproduzione, il tutto senza accedere all'attività dell'interfaccia utente della tua app. Infatti,
possono essere connesse contemporaneamente più app client allo stesso MediaSessionService
, ciascuna con il proprio MediaController
.
Implementare il ciclo di vita del servizio
Devi implementare due metodi di ciclo di vita del servizio:
onCreate()
viene chiamato quando il primo controller sta per connettersi e il servizio viene creato e avviato. È il posto migliore per crearePlayer
eMediaSession
.onDestroy()
viene chiamato quando il servizio viene interrotto. Tutte le risorse, inclusi il player e la sessione, devono essere rilasciate.
Se vuoi, puoi ignorare onTaskRemoved(Intent)
per personalizzare cosa succede quando l'utente chiude l'app dalle attività recenti. Per impostazione predefinita, il servizio viene lasciato in esecuzione se la riproduzione è in corso e viene interrotto in caso contrario.
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() } // 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(); } // Remember to release the player and media session in onDestroy @Override public void onDestroy() { mediaSession.getPlayer().release(); mediaSession.release(); mediaSession = null; super.onDestroy(); } }
In alternativa a mantenere la riproduzione in background, puoi interrompere il servizio in qualsiasi momento quando l'utente chiude l'app:
Kotlin
override fun onTaskRemoved(rootIntent: Intent?) { pauseAllPlayersAndStopSelf() }
Java
@Override public void onTaskRemoved(@Nullable Intent rootIntent) { pauseAllPlayersAndStopSelf(); }
Per qualsiasi altra implementazione manuale di onTaskRemoved
, puoi utilizzare
isPlaybackOngoing()
per verificare se la riproduzione è considerata in corso e se il servizio in primo piano è avviato.
Fornire l'accesso alla sessione multimediale
Sostituisci il metodo onGetSession()
per concedere ad altri client l'accesso alla sessione media che è stata creata al momento della creazione del servizio.
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; } }
Dichiara il servizio nel manifest
Un'app richiede le autorizzazioni FOREGROUND_SERVICE
e FOREGROUND_SERVICE_MEDIA_PLAYBACK
per eseguire un servizio in primo piano per la riproduzione:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
Devi anche dichiarare la classe Service
nel file manifest con un filtro per intent di MediaSessionService
e un foregroundServiceType
che includa mediaPlayback
.
<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>
Controllare la riproduzione utilizzando un MediaController
Nell'attività o nel frammento contenente l'interfaccia utente del player, puoi stabilire un collegamento tra l'interfaccia utente e la sessione multimediale utilizzando un MediaController
. L'interfaccia utente utilizza il media controller per inviare comandi dall'interfaccia utente al player all'interno della sessione. Per maggiori dettagli sulla creazione e sull'utilizzo di un MediaController
, consulta la guida Creare un MediaController
.
Gestire i comandi MediaController
Il MediaSession
riceve i comandi dal controller tramite il suo
MediaSession.Callback
. L'inizializzazione di un MediaSession
crea un'implementazione predefinita di MediaSession.Callback
che gestisce automaticamente tutti i comandi inviati da un MediaController
al player.
Notifica
Un MediaSessionService
crea automaticamente un MediaNotification
che dovrebbe funzionare nella maggior parte dei casi. Per impostazione predefinita, la notifica pubblicata è una notifica MediaStyle
che viene aggiornata con le informazioni più recenti della sessione multimediale e mostra i controlli di riproduzione. MediaNotification
è a conoscenza della tua sessione e può essere utilizzato per controllare la riproduzione di qualsiasi altra app collegata alla stessa sessione.
Ad esempio, un'app di streaming musicale che utilizza un MediaSessionService
crea un
MediaNotification
che mostra il titolo, l'artista e la copertina dell'elemento media corrente in riproduzione insieme ai controlli di riproduzione in base alla configurazione del
MediaSession
.
I metadati richiesti possono essere forniti nei contenuti multimediali o dichiarati come parte dell'elemento multimediale, come nello snippet seguente:
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();
Ciclo di vita delle notifiche
La notifica viene creata non appena Player
ha MediaItem
istanze
nella sua playlist.
Tutti gli aggiornamenti delle notifiche vengono eseguiti automaticamente in base allo stato Player
e
MediaSession
.
La notifica non può essere rimossa mentre il servizio in primo piano è in esecuzione. Per rimuovere immediatamente la notifica, devi chiamare Player.release()
o cancellare la playlist utilizzando Player.clearMediaItems()
.
Se il player viene messo in pausa, fermato o non funziona per più di 10 minuti senza altre interazioni dell'utente, il servizio esce automaticamente dallo stato di servizio in primo piano in modo da poter essere distrutto dal sistema. Puoi implementare la ripresa della riproduzione per consentire a un utente di riavviare il ciclo di vita del servizio e riprendere la riproduzione in un secondo momento.
Personalizzazione delle notifiche
I metadati dell'elemento attualmente in riproduzione possono essere personalizzati modificando
MediaItem.MediaMetadata
. Se vuoi aggiornare i metadati di un elemento esistente, puoi utilizzare Player.replaceMediaItem
per aggiornarli senza interrompere la riproduzione.
Puoi anche personalizzare alcuni dei pulsanti mostrati nella notifica impostando le preferenze dei pulsanti multimediali personalizzati per i controlli multimediali di Android. Scopri di più sulla personalizzazione dei controlli multimediali di Android.
Per personalizzare ulteriormente la notifica stessa, crea un
MediaNotification.Provider
con DefaultMediaNotificationProvider.Builder
o creando un'implementazione personalizzata dell'interfaccia del provider. Aggiungi il tuo fornitore a MediaSessionService
con setMediaNotificationProvider
.
Ripristino della riproduzione
Dopo la terminazione del MediaSessionService
e anche dopo il riavvio del dispositivo, è possibile offrire la ripresa della riproduzione per consentire agli utenti di riavviare il servizio e riprendere la riproduzione da dove avevano interrotto. Per impostazione predefinita, la ripresa della riproduzione è disattivata. Ciò significa che l'utente non può riprendere la riproduzione quando il servizio non è in esecuzione. Per attivare questa funzionalità, devi dichiarare un ricevitore dei pulsanti multimediali e implementare il metodo onPlaybackResumption
.
Dichiara il ricevitore del pulsante multimediale Media3
Inizia dichiarando MediaButtonReceiver
nel file manifest:
<receiver android:name="androidx.media3.session.MediaButtonReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
Implementare il callback di ripresa della riproduzione
Quando la ripresa della riproduzione viene richiesta da un dispositivo Bluetooth o dalla funzionalità di ripresa dell'interfaccia utente di sistema Android, viene chiamato il metodo di callback onPlaybackResumption()
.
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; }
Se hai memorizzato altri parametri, come la velocità di riproduzione, la modalità di ripetizione o la modalità di riproduzione casuale, onPlaybackResumption()
è un buon punto di partenza per configurare il player con questi parametri prima che Media3 prepari il player e avvii la riproduzione al termine del callback.
Configurazione avanzata del controller e compatibilità con le versioni precedenti
Uno scenario comune è l'utilizzo di un MediaController
nell'interfaccia utente dell'app per controllare la riproduzione e visualizzare la playlist. Allo stesso tempo, la sessione è esposta a client esterni come i controlli multimediali e l'assistente Android su dispositivi mobili o TV, Wear OS per gli orologi e Android Auto nelle auto. L'app demo della sessione Media3 è un esempio di app che implementa questo scenario.
Questi client esterni potrebbero utilizzare API come MediaControllerCompat
della libreria AndroidX precedente o android.media.session.MediaController
della piattaforma Android. Media3 è completamente compatibile con le versioni precedenti della libreria e offre interoperabilità con l'API della piattaforma Android.
Utilizzare il controller delle notifiche multimediali
È importante capire che questi controller legacy e della piattaforma condividono lo stesso stato e la visibilità non può essere personalizzata in base al controller (ad esempio PlaybackState.getActions()
e PlaybackState.getCustomActions()
disponibili). Puoi utilizzare il controller di notifica multimediale per configurare lo stato impostato nella sessione multimediale della piattaforma per la compatibilità con questi controller legacy e della piattaforma.
Ad esempio, un'app può fornire un'implementazione di MediaSession.Callback.onConnect()
per impostare i comandi disponibili e le preferenze dei pulsanti multimediali specificamente per la sessione della piattaforma come segue:
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 button preferences and commands to configure the platform session. return AcceptedResultBuilder(session) .setMediaButtonPreferences( ImmutableList.of( createSeekBackwardButton(customCommandSeekBackward), createSeekForwardButton(customCommandSeekForward)) ) .setAvailablePlayerCommands(playerCommands) .setAvailableSessionCommands(sessionCommands) .build() } // Default commands with default button preferences 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 button preferences and commands to configure the platform session. return new AcceptedResultBuilder(session) .setMediaButtonPreferences( ImmutableList.of( createSeekBackwardButton(customCommandSeekBackward), createSeekForwardButton(customCommandSeekForward))) .setAvailablePlayerCommands(playerCommands) .setAvailableSessionCommands(sessionCommands) .build(); } // Default commands with default button preferences for all other controllers. return new AcceptedResultBuilder(session).build(); }
Autorizzare Android Auto a inviare comandi personalizzati
Quando utilizzi un MediaLibraryService
per supportare Android Auto con l'app mobile, il controller Android Auto richiede comandi disponibili appropriati, altrimenti Media3 rifiuterà i comandi personalizzati in arrivo da quel controller:
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 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 for all other controllers. return new AcceptedResultBuilder(session).build(); }
L'app demo della sessione ha un modulo per auto e motori, che dimostra il supporto per il sistema operativo Automotive che richiede un APK separato.