Vous pouvez lire des contenus multimédias en arrière-plan, même lorsque votre application n'est pas à l'écran, par exemple lorsque l'utilisateur interagit avec d'autres applications.
Pour ce faire, vous devez intégrer le MediaPlayer dans un service MediaBrowserServiceCompat
et le faire interagir avec un MediaBrowserCompat
dans une autre activité.
Soyez prudent lors de l'implémentation de cette configuration client et serveur. Des attentes existent concernant la façon dont un lecteur exécuté dans un service en arrière-plan interagit avec le reste du système. Si votre application ne répond pas à ces attentes, l'utilisateur risque de vivre une mauvaise expérience. Pour en savoir plus, consultez Créer une application audio.
Cette page décrit des instructions spéciales pour gérer un MediaPlayer lorsque vous l'implémentez dans un service.
Exécuter de manière asynchrone
Comme pour un Activity
, toutes les tâches d'un Service
sont effectuées dans un seul thread par défaut. En fait, lorsque vous exécutez une activité et un service à partir de la même application, ils utilisent le même thread (le "thread principal") par défaut.
Les services doivent traiter rapidement les intents entrants et ne jamais effectuer de calculs longs lorsqu'ils y répondent. Vous devez effectuer toute tâche lourde ou appel bloquant de manière asynchrone: soit à partir d'un autre thread que vous implémentez vous-même, soit à l'aide des nombreuses fonctionnalités du framework pour le traitement asynchrone.
Par exemple, lorsque vous utilisez MediaPlayer
à partir de votre thread principal, vous devez:
- Appelez
prepareAsync()
au lieu deprepare()
, et - Implémentez un
MediaPlayer.OnPreparedListener
pour être informé lorsque la préparation est terminée et que vous pouvez commencer à jouer.
Exemple :
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();
}
}
Gérer les erreurs asynchrones
Pour les opérations synchrones, les erreurs sont signalées par une exception ou un code d'erreur. Toutefois, lorsque vous utilisez des ressources asynchrones, vous devez informer votre application des erreurs de manière appropriée. Dans le cas d'un MediaPlayer
, vous implémentez un MediaPlayer.OnErrorListener
et le définissez dans votre instance 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!
}
}
Lorsqu'une erreur se produit, MediaPlayer
passe à l'état Error (Erreur). Vous devez le réinitialiser avant de pouvoir l'utiliser à nouveau. Pour en savoir plus, consultez le diagramme des états complet de la classe MediaPlayer
.
Utiliser des wakelocks
Lorsque vous écoutez ou diffusez de la musique en arrière-plan, vous devez utiliser des wake locks pour empêcher le système d'interférer avec la lecture, par exemple en mettant l'appareil en veille.
Un verrouillage de réveil est un signal envoyé au système indiquant que votre application utilise des fonctionnalités qui doivent rester disponibles même lorsque le téléphone est inactif.
Pour vous assurer que le processeur continue de s'exécuter pendant la lecture de votre MediaPlayer
, appelez la méthode setWakeMode()
lorsque vous initialisez votre MediaPlayer
. MediaPlayer
maintient le verrouillage spécifié pendant la lecture et le libère en cas de mise en pause ou d'arrêt:
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);
Toutefois, le verrouillage de réveil acquis dans cet exemple ne garantit que le maintien du processeur. Si vous diffusez des contenus multimédias sur le réseau et que vous utilisez le Wi-Fi, vous souhaitez probablement également conserver un WifiLock
, que vous devez acquérir et libérer manuellement. Par conséquent, lorsque vous commencez à préparer MediaPlayer
avec l'URL distante, vous devez créer et acquérir le verrouillage Wi-Fi.
Exemple :
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();
Lorsque vous mettez en pause ou arrêtez votre contenu multimédia, ou lorsque vous n'avez plus besoin du réseau, vous devez déverrouiller le réseau:
Kotlin
wifiLock.release()
Java
wifiLock.release();
Effectuer un nettoyage
Comme indiqué précédemment, un objet MediaPlayer
peut consommer une quantité importante de ressources système. Vous ne devez donc le conserver que le temps nécessaire et appeler release()
lorsque vous avez terminé. Il est important d'appeler explicitement cette méthode de nettoyage plutôt que de s'appuyer sur la collecte de place du système, car il peut s'écouler un certain temps avant que le collecteur de place ne récupère le MediaPlayer
, car il n'est sensible qu'aux besoins en mémoire et non à la pénurie d'autres ressources liées aux médias. Par conséquent, si vous utilisez un service, vous devez toujours remplacer la méthode onDestroy()
pour vous assurer de libérer 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();
}
}
Vous devez toujours rechercher d'autres opportunités de libérer votre MediaPlayer
, en plus de le libérer lors de l'arrêt. Par exemple, si vous ne pouvez pas lire de contenu multimédia pendant une longue période (après avoir perdu la sélection audio, par exemple), vous devez libérer votre MediaPlayer
existante et la recréer plus tard. En revanche, si vous ne prévoyez d'arrêter la lecture que pendant une très courte période, vous devez probablement conserver votre MediaPlayer
pour éviter les frais liés à sa création et à sa préparation.
En savoir plus
Jetpack Media3 est la solution recommandée pour la lecture multimédia dans votre application. En savoir plus
Ces pages traitent des sujets liés à l'enregistrement, au stockage et à la lecture d'audio et de vidéo: