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:
- Chiamare
prepareAsync()anzichéprepare()e - Implementare un
MediaPlayer.OnPreparedListenerper ricevere una notifica al termine della preparazione e poter iniziare la riproduzione.
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: