Lire des contenus multimédias en arrière-plan

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:

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: