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 servizio MediaBrowserServiceCompat
e fai in modo che interagisca con un MediaBrowserCompat
in un'altra attività.
Fai attenzione a implementare questa configurazione del client e del server. Esistono aspettative su come 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 istruzioni speciali per la gestione di un MediaPlayer quando lo implementi all'interno di un servizio.
Eseguire in modo asincrono
Come per un Activity
, per impostazione predefinita tutto il lavoro in un Service
viene eseguito in un singolo thread. Infatti, quando esegui un'attività e un servizio dalla stessa
applicazione, per impostazione predefinita utilizzano lo stesso thread ("thread principale").
I servizi devono elaborare rapidamente gli intent in arrivo e non eseguire mai calcoli lunghi quando rispondono. Devi eseguire qualsiasi lavoro pesante o chiamate bloccanti in modo asincrono: da un altro thread che implementi autonomamente o utilizzando le numerose strutture del framework per l'elaborazione asincrona.
Ad esempio, quando utilizzi MediaPlayer
dal thread principale, devi:
- Chiama
prepareAsync()
anzichéprepare()
e - Implementa un
MediaPlayer.OnPreparedListener
per ricevere una notifica quando la preparazione è completata e puoi iniziare a giocare.
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 informare la tua applicazione in modo appropriato degli errori. Nel caso di un MediaPlayer
, implementa un MediaPlayer.OnErrorListener
e impostalo 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, MediaPlayer
passa allo stato Error. Devi reimpostarlo prima di poterlo utilizzare di nuovo. Per maggiori dettagli, consulta il diagramma di stato completo per la classe MediaPlayer
.
Utilizzare i wakelock
Quando riproduci o riproduci in streaming musica in background, devi utilizzare i wakelock per impedire al sistema di interferire con la riproduzione, ad esempio facendo andare in sospensione il dispositivo.
Un blocco di attivazione è 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 funzionare durante la riproduzione di MediaPlayer
, chiama il metodo setWakeMode()
quando inizializzazione il MediaPlayer
. Il tasto MediaPlayer
mantiene la nota bloccata durante la riproduzione e la rilascia quando viene messa in pausa o interrotta:
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 riproducendo contenuti multimediali in streaming sulla rete e utilizzi il Wi-Fi, ti consigliamo di acquisire anche un WifiLock
, che dovrai 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 la riproduzione dei contenuti multimediali o quando non hai più bisogno della rete, devi rilasciare il blocco:
Kotlin
wifiLock.release()
Java
wifiLock.release();
Esegui la pulizia
Come accennato in precedenza, un oggetto MediaPlayer
può consumare una quantità significativa di risorse di sistema, quindi devi mantenerlo solo per il tempo necessario e chiamare release()
al termine. È importante chiamare questo metodo di pulizia in modo esplicito anziché fare affidamento sulla raccolta dei rifiuti di sistema, perché potrebbe essere necessario del tempo prima che il garbage collector recuperi MediaPlayer
, poiché è sensibile solo alle esigenze di memoria e non alla carenza di altre risorse correlate ai contenuti multimediali. Pertanto, se utilizzi un servizio,
devi sempre sostituire il metodo onDestroy()
per assicurarti di liberare il 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 rilasciarlo quando viene spento, dovresti sempre cercare altre opportunità per rilasciare il tuo
MediaPlayer
. Ad esempio, se prevedi di non poter riprodurre contenuti multimediali per un periodo di tempo prolungato (ad esempio dopo aver perso l'attenzione audio), devi assolutamente rilasciare il tuo MediaPlayer
esistente e crearlo di nuovo in un secondo momento. D'altra parte, se prevedi di interrompere la riproduzione solo per poco tempo, ti consigliamo di conservare il tuo MediaPlayer
per evitare il sovraccarico di dover creare e preparare di nuovo il tuo canale.
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, allo stoccaggio e alla riproduzione di audio e video: