Riprodurre contenuti multimediali in background

Puoi riprodurre contenuti multimediali in background anche quando l'applicazione non è sullo schermo, ad esempio mentre l'utente interagisce con altre applicazioni.

Per farlo, incorpora MediaPlayer in un MediaBrowserServiceCompat servizio e lo fai interagire con un MediaBrowserCompat in un'altra attività.

Fai attenzione quando implementi questa configurazione client e server. Esistono aspettative sul modo in cui un player in esecuzione in un servizio in background interagisce con il resto del sistema. Se la tua applicazione non soddisfa queste aspettative, l'utente potrebbe avere un'esperienza negativa. Per maggiori dettagli, consulta Creare un'app audio.

Questa pagina descrive le istruzioni speciali per la gestione di MediaPlayer quando lo implementi all'interno di un servizio.

Eseguire in modo asincrono

Come per un Activity, tutto il lavoro in un Service viene eseguito in un singolo thread per impostazione predefinita. Infatti, quando esegui un'attività e un servizio dalla stessa applicazione, per impostazione predefinita utilizzano lo stesso thread (il "thread principale").

I servizi devono elaborare rapidamente gli intent in entrata e non devono mai eseguire calcoli lunghi quando rispondono. Devi eseguire qualsiasi lavoro pesante o chiamate di blocco in modo asincrono: da un altro thread che implementi tu stesso o utilizzando le numerose funzionalità del framework per l'elaborazione asincrona.

Ad esempio, quando utilizzi MediaPlayer dal thread principale, devi:

Ad esempio:

Kotlin

private const val ACTION_PLAY: String = "com.example.action.PLAY"

class MyService: Service(), MediaPlayer.OnPreparedListener {

    private var mMediaPlayer: MediaPlayer? = null

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        ...
        val action: String = intent.action
        when(action) {
            ACTION_PLAY -> {
                mMediaPlayer = ... // initialize it here
                mMediaPlayer?.apply {
                    setOnPreparedListener(this@MyService)
                    prepareAsync() // prepare async to not block main thread
                }

            }
        }
        ...
    }

    /** Called when MediaPlayer is ready */
    override fun onPrepared(mediaPlayer: MediaPlayer) {
        mediaPlayer.start()
    }
}

Java

public class MyService extends Service implements MediaPlayer.OnPreparedListener {
    private static final String ACTION_PLAY = "com.example.action.PLAY";
    MediaPlayer mediaPlayer = null;

    public int onStartCommand(Intent intent, int flags, int startId) {
        ...
        if (intent.getAction().equals(ACTION_PLAY)) {
            mediaPlayer = ... // initialize it here
            mediaPlayer.setOnPreparedListener(this);
            mediaPlayer.prepareAsync(); // prepare async to not block main thread
        }
    }

    /** Called when MediaPlayer is ready */
    public void onPrepared(MediaPlayer player) {
        player.start();
    }
}

Gestire gli errori asincroni

Nelle operazioni sincrone, gli errori vengono segnalati con un'eccezione o un codice di errore. Tuttavia, quando utilizzi risorse asincrone, devi notificare gli errori alla tua applicazione in modo appropriato. Nel caso di un MediaPlayer, implementa un MediaPlayer.OnErrorListener e lo imposti nell'istanza MediaPlayer:

Kotlin

class MyService : Service(), MediaPlayer.OnErrorListener {

    private var mediaPlayer: MediaPlayer? = null

    fun initMediaPlayer() {
        // ...initialize the MediaPlayer here...
        mediaPlayer?.setOnErrorListener(this)
    }

    override fun onError(mp: MediaPlayer, what: Int, extra: Int): Boolean {
        // ... react appropriately ...
        // The MediaPlayer has moved to the Error state, must be reset!
    }
}

Java

public class MyService extends Service implements MediaPlayer.OnErrorListener {
    MediaPlayer mediaPlayer;

    public void initMediaPlayer() {
        // ...initialize the MediaPlayer here...
        mediaPlayer.setOnErrorListener(this);
    }

    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {
        // ... react appropriately ...
        // The MediaPlayer has moved to the Error state, must be reset!
    }
}

Quando si verifica un errore, il MediaPlayer passa allo stato Errore. Devi reimpostarlo prima di poterlo utilizzare di nuovo. Per maggiori dettagli, consulta il diagramma di stato completo per la MediaPlayer classe.

Utilizzare i wakelock

Quando riproduci o trasmetti musica in streaming in background, devi utilizzare i wakelock per impedire al sistema di interferire con la riproduzione, ad esempio mettendo il dispositivo in modalità di sospensione.

Un wakelock è un segnale al sistema che indica che la tua applicazione utilizza funzionalità che devono rimanere disponibili anche quando lo smartphone è inattivo.

Per assicurarti che la CPU continui a essere in esecuzione mentre MediaPlayer è in riproduzione, chiama il metodo setWakeMode() quando inizializzi MediaPlayer. Il MediaPlayer mantiene il blocco specificato durante la riproduzione e lo rilascia quando viene messo in pausa o interrotto:

Kotlin

mediaPlayer = MediaPlayer().apply {
    // ... other initialization here ...
    setWakeMode(applicationContext, PowerManager.PARTIAL_WAKE_LOCK)
}

Java

mediaPlayer = new MediaPlayer();
// ... other initialization here ...
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);

Tuttavia, il wakelock acquisito in questo esempio garantisce solo che la CPU rimanga attiva. Se stai trasmettendo contenuti multimediali in streaming sulla rete e utilizzi il Wi-Fi, probabilmente ti consigliamo di mantenere anche un WifiLock, che devi acquisire e rilasciare manualmente. Pertanto, quando inizi a preparare il MediaPlayer con l'URL remoto, devi creare e acquisire il blocco Wi-Fi.

Ad esempio:

Kotlin

val wifiManager = getSystemService(Context.WIFI_SERVICE) as WifiManager
val wifiLock: WifiManager.WifiLock =
    wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock")

wifiLock.acquire()

Java

WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
    .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");

wifiLock.acquire();

Quando metti in pausa o interrompi i contenuti multimediali o quando non hai più bisogno della rete, devi rilasciare il blocco:

Kotlin

wifiLock.release()

Java

wifiLock.release();

Eseguire la pulizia

Come accennato in precedenza, un oggetto MediaPlayer può consumare una quantità significativa di risorse di sistema, quindi devi conservarlo solo per il tempo necessario e chiamare release() quando hai finito di utilizzarlo. È importante chiamare questo metodo di pulizia in modo esplicito anziché affidarsi alla garbage collection del sistema, perché potrebbe essere necessario del tempo prima che il garbage collector recuperi MediaPlayer, in quanto è sensibile solo alle esigenze di memoria e non alla carenza di altre risorse correlate ai contenuti multimediali. Pertanto, nel caso in cui utilizzi un servizio, devi sempre sostituire il metodo onDestroy() per assicurarti di rilasciare MediaPlayer:

Kotlin

class MyService : Service() {

    private var mediaPlayer: MediaPlayer? = null
    // ...

    override fun onDestroy() {
        super.onDestroy()
        mediaPlayer?.release()
    }
}

Java

public class MyService extends Service {
   MediaPlayer mediaPlayer;
   // ...

   @Override
   public void onDestroy() {
       super.onDestroy();
       if (mediaPlayer != null) mediaPlayer.release();
   }
}

Oltre a rilasciare MediaPlayer quando viene chiuso, devi sempre cercare altre opportunità per rilasciarlo. Ad esempio, se prevedi di non poter riprodurre contenuti multimediali per un periodo di tempo prolungato (ad esempio dopo aver perso lo stato attivo audio), devi assolutamente rilasciare esistente MediaPlayer e crearlo di nuovo in un secondo momento. D'altra parte, se prevedi di interrompere la riproduzione solo per un periodo di tempo molto breve, probabilmente ti consigliamo di conservare MediaPlayer per evitare il sovraccarico di doverlo creare e preparare di nuovo.

Scopri di più

Jetpack Media3 è la soluzione consigliata per la riproduzione di contenuti multimediali nella tua app. Scopri di più.

Queste pagine trattano argomenti relativi alla registrazione, all'archiviazione e alla riproduzione di audio e video: