Riproduzione in background con MediaSessionService

Spesso è preferibile riprodurre contenuti multimediali mentre un'app non è in primo piano. Ad esempio, un lettore musicale in genere continua a riprodurre musica quando l'utente ha bloccato il dispositivo o sta usando un'altra app. La libreria Media3 fornisce una serie di interfacce che consentono di supportare la riproduzione in background.

Utilizzare un MediaSessionService

Per abilitare la riproduzione in background, devi includere Player e MediaSession all'interno di un Service separato. In questo modo, il dispositivo può continuare a riprodurre contenuti multimediali anche quando l'app non è in primo piano.

MediaSessionService consente l'esecuzione della sessione multimediale separatamente
  dall'attività dell'app
Figura 1: MediaSessionService consente alla sessione multimediale di essere eseguita separatamente dall'attività dell'app

Quando ospiti un giocatore all'interno di un Servizio, devi utilizzare un MediaSessionService. Per farlo, crea una classe che estenda MediaSessionService e crea la sessione multimediale al suo interno.

L'utilizzo di MediaSessionService consente a client esterni come l'assistente Google, i controlli multimediali di sistema, i pulsanti multimediali sui dispositivi periferici o i dispositivi complementari 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 più app client allo stesso MediaSessionService contemporaneamente, ognuna con il proprio MediaController.

Implementa il ciclo di vita del servizio

Devi implementare due metodi del ciclo di vita del tuo servizio:

  • onCreate() viene chiamato quando il primo controller sta per connettersi e il servizio viene istanziato e avviato. È il posto migliore per creare Player e MediaSession.
  • onDestroy() viene chiamato quando il servizio viene interrotto. Tutte le risorse, inclusi giocatore e 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 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;
  }
}

In alternativa alla riproduzione continua in background, puoi interrompere il servizio in ogni caso quando l'utente chiude l'app:

Kotlin

@OptIn(UnstableApi::class)
override fun onTaskRemoved(rootIntent: Intent?) {
  pauseAllPlayersAndStopSelf()
}

Java

@OptIn(markerClass = UnstableApi.class)
@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 è stato avviato.

Fornire l'accesso alla sessione multimediale

Esegui l'override del metodo onGetSession() per consentire ad altri client di accedere alla sessione multimediale creata al momento della creazione del servizio.

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;
  }
}

Dichiara il servizio nel manifest

Un'app richiede le autorizzazioni FOREGROUND_SERVICE e FOREGROUND_SERVICE_MEDIA_PLAYBACK per eseguire un servizio in primo piano di 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 manifest con un filtro per intent di MediaSessionService e un foregroundServiceType che include 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 fragment contenente la UI del player, puoi stabilire un collegamento tra la UI e la sessione multimediale utilizzando un MediaController. La tua UI utilizza il controller multimediale per inviare comandi dalla tua UI al player all'interno della sessione. Per informazioni dettagliate 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 che un MediaController invia al tuo 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. Il MediaNotification è a conoscenza della tua sessione e può essere utilizzato per controllare la riproduzione per qualsiasi altra app connessa alla stessa sessione.

Ad esempio, un'app di streaming musicale che utilizza un MediaSessionService creerebbe un MediaNotification che mostra il titolo, l'artista e la copertina dell'album dell'elemento multimediale in riproduzione insieme ai controlli di riproduzione in base alla configurazione di MediaSession.

I metadati richiesti possono essere forniti nel media o dichiarati come parte dell'elemento multimediale, come nello snippet seguente:

Kotlin

val mediaItem =
  MediaItem.