Archives mensuelles: December 2015

Algorithmique Revue de presse

TP Android : Les sons (musique en tache de fond)

L’objectif de ce TP est le même que le TP précédent, c’est à dire réaliser un petit player aléatoire. La différence est que l’on souhaite continuer à jouer la musique quand l’activité n’est plus visible, et même quand l’écran passe en veille. Pour cela, il nous faudra utiliser un service.

Mise en place

Ce TP nécessite quelques préparations :

  • Créer une activité avec au moins deux boutons, d’identifiant R.id.buttonStart et R.id.buttonStop
  • Ajouter les autorisations nécessaires dans le fichier manifest.xml, juste avant la balise application :
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />

    La première ligne autorise l’accès aux fichiers son (sur stockage externe). La deuxième demande la permission de garder le périphérique éveillé, afin de ne pas couper la musique lors de la mise en veille.

  • Il faut ensuite la classe AudioPlayer (la même que dans le TP précédent), dont voici le code :
    package// N'oubliez pas de laisser votre nom de paquet
     
    import android.content.Context;
    import android.media.AudioManager;
    import android.media.MediaPlayer;
    import android.net.Uri;
     
    public class AudioPlayer implements AudioManager.OnAudioFocusChangeListener {
     
        private static AudioPlayer audioPlayer; // On souhaite un seul audioPlayer pour l'application, quoi qu'il arrive
     
        private Context context;
        private MediaPlayer mediaPlayer;
        private Uri lastUri;
        private MediaPlayer.OnCompletionListener onCompletionlistener;
     
        public void setOnCompletionlistener(MediaPlayer.OnCompletionListener onCompletionlistener) {
            this.onCompletionlistener = onCompletionlistener;
            if (mediaPlayer != null) mediaPlayer.setOnCompletionListener(onCompletionlistener);
        }
     
        public static AudioPlayer get(Context context){
            if (audioPlayer == null) {
                audioPlayer = new AudioPlayer(context);
            }
     
            // On demande à gérer les évènements liés au son
            AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
            int result = audioManager.requestAudioFocus(audioPlayer, AudioManager.STREAM_MUSIC,
                    AudioManager.AUDIOFOCUS_GAIN);
     
            if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
                release();
                // could not get audio focus.
            }
     
            return audioPlayer;
        }
     
        public static void release(){
            if (audioPlayer == null) return;
            if (audioPlayer.mediaPlayer == null) return; // déjà libéré
     
            if (audioPlayer.mediaPlayer.isPlaying()) audioPlayer.mediaPlayer.stop();
            audioPlayer.mediaPlayer.release(); // libère le mediaplayer
            audioPlayer.mediaPlayer = null;  // Précise que le mediaplayer a été libéré
        }
     
        private AudioPlayer(Context context) {
            this.context = context;
        }
     
        public void loadUri(Context context, Uri uri) {
            if (uri != null) {
                this.context = context;
                this.lastUri = uri;
            }
            load();
        }
     
        private void load() {
            if (lastUri == null) return;
            if (mediaPlayer != null) {
                // Si on joue déjà qq chose
                if (mediaPlayer.isPlaying()) {
                    mediaPlayer.stop(); // On abandonne le morceau en cours
                }
                mediaPlayer.release(); // On libère l'ancien
            }
            // On sélectionne le nouveau morceau
            mediaPlayer = MediaPlayer.create(context, lastUri);
            if (onCompletionlistener != null)
                this.mediaPlayer.setOnCompletionListener(onCompletionlistener);
        }
     
        public void onAudioFocusChange(int focusChange) {
            switch (focusChange) {
                case AudioManager.AUDIOFOCUS_GAIN:
                    // resume playback
                    if (mediaPlayer == null) load(); // Si le mediaPlayer n'est pas configuré, on le configure
                    else if (!mediaPlayer.isPlaying()) mediaPlayer.start(); // Sinon on reprend où on en était
                    mediaPlayer.setVolume(1.0f, 1.0f); // On remet le volume à 100%
                    break;
     
                case AudioManager.AUDIOFOCUS_LOSS:
                    // Lost focus for an unbounded amount of time: stop playback and release media player
                    if (mediaPlayer == null) break; // Déjà libéré, ne rien faire
                    if (mediaPlayer.isPlaying()) mediaPlayer.stop(); // On arrête la musique en cours
                    release();
                    break;
     
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                    // Lost focus for a short time, but we have to stop
                    // playback. We don't release the media player because playback
                    // is likely to resume
                    if (mediaPlayer == null) break;
                    if (mediaPlayer.isPlaying()) mediaPlayer.pause();
                    break;
     
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                    // Lost focus for a short time, but it's ok to keep playing
                    // at an attenuated level
                    if (mediaPlayer == null) break;
                    if (mediaPlayer.isPlaying()) mediaPlayer.setVolume(0.1f, 0.1f);
                    break;
            }
        }
     
        public void play() {
            if (mediaPlayer == null || mediaPlayer.isPlaying()) load();
            if (mediaPlayer != null) {
                mediaPlayer.start();
            }
        }
     
        public void stop() {
            mediaPlayer.stop();
            mediaPlayer.release();
            mediaPlayer=null;
        }
     
        public void pause() {
            mediaPlayer.pause();
        }
    }

Création du service.

Un service est en gros une activité sans interface graphique. Utilisez l’assistant pour créer un nouveau service nommé MusicService.

On ajoute les champs et constantes suivants :

    private  AudioPlayer audioPlayer;
    private String title;
 
    public static final String COMMAND = "command";
    public static final String START = "start";
    public static final String STOP = "stop";

Pour gérer les commandes qui seront envoyées par l’activité, on ajoute la méthode onStartCommand() :

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent == null) return Service.START_STICKY;
        String command = intent.getExtras().getString(COMMAND);
        if (START.equals(command)) { // On joue un morceau si cela a été demandé
            audioPlayer = AudioPlayer.get(this);
            audioPlayer.setOnCompletionlistener(this);
            selectAudioFile();
            audioPlayer.play();
        } else if (STOP.equals(command)){ // On libère les ressources si nécessaire
            AudioPlayer.release();
        }
        return START_STICKY;
    }

Sélectionner un fichier aléatoire se fait de la même manière que dans le TP précédent :

    private void selectAudioFile() {
        //On demande la liste des fichiers audios disponibles
        ContentResolver contentResolver = getContentResolver();
        Uri uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
        Cursor cursor = contentResolver.query(uri, null, null, null, null);
        if (cursor == null) {
            // query failed, handle error.
        } else if (!cursor.moveToFirst()) {
            // no media on the device
        } else {
            int ndx = (int) (cursor.getCount()*Math.random());
            cursor.move(ndx);
 
            int titleColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE);
            int idColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID);
 
            long thisId = cursor.getLong(idColumn);
            this.title = cursor.getString(titleColumn);
 
            Uri contentUri = ContentUris.withAppendedId(
                    android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, thisId);
 
            audioPlayer.loadUri(this, contentUri);
        }
        cursor.close();
    }

Il ne reste plus qu’à faire ajouter la méthode pour écouter l’évènement onCompletion() (fin de morceau) (alt-entrée et choisir implements…)
Le plus simple est demander un nouveau morceau aléatoire :

    @Override
    public void onCompletion(MediaPlayer mediaPlayer) {
        selectAudioFile();
        audioPlayer.play();
    }

Appel du service

Dans le code de l’activité, on configure dans le onCreate() les actions des boutons :

        Button buttonStart = (Button) findViewById(R.id.buttonStart);
        buttonStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent music = new Intent(MainActivity.this,MusicService.class);
                music.putExtra(MusicService.COMMAND, MusicService.START);
                startService(music);
            }
        });
 
        Button buttonStop = (Button) findViewById(R.id.buttonStop);
        buttonStop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent music = new Intent(MainActivity.this,MusicService.class);
                music.putExtra(MusicService.COMMAND, MusicService.STOP);
                startService(music);
            }
        });

Le service est maintenant opérationnel.

Interdire la mise en veille

Dans le onStartCommand(), on va créer (si il n’existe pas déjà) un objet qui servira au verrouillage de la mise en veille :

if (wakeLock == null) {
            PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
            wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MusicLockTag");
        }

Comme d’habitude, importez les classes concernées, et ajoutez wakeLock comme champ.

Toujours dans onStartCommand(), il faut maintenant ajouter le verrouillage/déverrouillage proprement dit :

            wakeLock.acquire();

juste avant l’appel à play()

           wakeLock.release();

juste après l’appel au release de l’AudioPlayer.

Il faut aussi penser à déverrouiller la mise en veille si on quitte le service :

    @Override
    public void onDestroy() {
        super.onDestroy();
        wakeLock.release();
    }

Notification

Pour indiquer que le service est important pour l’utilisateur, et donc qu’il faut si possible éviter de l’arrêter dès qu’une activité a besoin de mémoire, il faut associer une notification au service.

Les notifications nécessitant une icône, créez-en une avec l’assistant « image assets » en choisissant le type « notification icon ». Vous pouvez utiliser un des clipart, ou votre propre image comme source.

Ensuite, ajoutez une méthode créant la notification dans le code du service :

    public  void createNotification(){
// Crée un intenta permettant de lancer l'activité quand on clique sur la notification
        if (pi== null) pi = PendingIntent.getActivity( this, 0,
                new Intent(this, MainActivity.class),
                PendingIntent.FLAG_UPDATE_CURRENT);
 
        Notification notification = new Notification.Builder(getApplicationContext())
                .setContentTitle("Playing ") //Ces 3 champs
                .setContentText(title)           // sont
                .setSmallIcon(R.drawable.ic_stat_mus) // obligatoires
                .setContentIntent(pi)
                .setOngoing(true)
                .build();
 
        startForeground(1, notification); 
    }

Dans la méthode selectAudioFile(), appelez cette méthode (après avoir obtenu le nom du fichier joué). Dans le onStartCommand(), dans la branche stop de la conditionnelle et après les release(), demandez à quitter l’avant-plan avec la commande suivante :

stopForeground(true);

Références

La documentation de MediaPlayer (JavaDoc) et (guide sur Android Developer)

En complément, le TP sur les bruitages TP Android : Les sons (bruitages)
Le TP sur la musique TP Android : Les sons (musique)

Programmation Android

TP Android : Les sons (musique)

Sur Android, la musique se gère différemment des bruitages : une musique est trop longue pour être raisonnablement chargée en mémoire dans un format directement jouable, on ne joue pas plusieurs musiques en même temps et un petit temps de retard au lancement n’est pas très gênant.

Dans ce TP, nous allons voir comment jouer de la musique pendant qu’une activité est visible (jouer de la musique en tache de fond sera l’objet d’un autre TP). L’objectif est de réaliser un petit player permettant de jouer aléatoirement des morceaux de votre téléphone.

Jouer un morceau de musique dans une activité

Le code permettant de lancer une musique n’est pas très difficile en soi. Par contre, respecter les règles de bonne conduite d’une application jouant des sons longs est plus compliqué. C’est pourquoi, exceptionnellement, le code d’une classe facilitant le respect de ces règles sera fourni.

Préparation

Pour ce TP, il vous faut une activité avec un « textView » (pour le titre) et trois « button » (nouveau, jouer, arrêter).
Ajoutez une classe AudioPlayer et collez le code suivant dedans (gardez tout de même votre déclaration de package)

(Attention : l’appel à new AudioPlayer() posait des problèmes non signalés dans la documentation avec les version récentes d’Android. Cette mise à jour devrait corriger le problème, mais n’a pas été (encore) été testée sur un dispositif récent. Si vous testez, merci de m’indiquer si ça fonctionne pour vous )

package com.hexagonalgames.audioplayer; // Laissez votre package ici, ne copiez que la suite.
import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
 
public class AudioPlayer implements AudioManager.OnAudioFocusChangeListener {
 
    private static AudioPlayer audioPlayer; // On souhaite un seul audioPlayer pour l'application, quoi qu'il arrive
 
    private Context context;
    private MediaPlayer mediaPlayer;
    private Uri lastUri;
    private MediaPlayer.OnCompletionListener onCompletionlistener;
 
    public void setOnCompletionlistener(MediaPlayer.OnCompletionListener onCompletionlistener) {
        this.onCompletionlistener = onCompletionlistener;
        if (mediaPlayer != null) mediaPlayer.setOnCompletionListener(onCompletionlistener);
    }
 
    public static AudioPlayer get(Context context){
        if (audioPlayer == null) {
            audioPlayer = new AudioPlayer(context);
        }
 
        // On demande à gérer les évènements liés au son
        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        int result = audioManager.requestAudioFocus(audioPlayer, AudioManager.STREAM_MUSIC,
                AudioManager.AUDIOFOCUS_GAIN);
 
        if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
            release();
            // could not get audio focus.
        }
 
        return audioPlayer;
    }
 
    public static void release(){
        if (audioPlayer == null) return;
        if (audioPlayer.mediaPlayer == null) return; // déjà libéré
 
        audioPlayer.mediaPlayer.release(); // libère le mediaplayer
        audioPlayer.mediaPlayer = null;  // Précise que le mediaplayer a été libéré
    }
 
    private AudioPlayer(Context context) {
        this.context = context;
    }
 
    public void loadUri(Context context, Uri uri) {
        if (uri != null) {
            this.context = context;
            this.lastUri = uri;
        }
        load();
    }
 
    private void load() {
        if (lastUri == null) return;
        if (mediaPlayer != null) {
            // Si on joue déjà qq chose
            if (mediaPlayer.isPlaying()) {
                mediaPlayer.stop(); // On abandonne le morceau en cours
            }
            mediaPlayer.release(); // On libère l'ancien
        }
        // On sélectionne le nouveau morceau
        mediaPlayer = MediaPlayer.create(context, lastUri);
        if (onCompletionlistener != null)
            this.mediaPlayer.setOnCompletionListener(onCompletionlistener);
    }
 
    public void onAudioFocusChange(int focusChange) {
        switch (focusChange) {
            case AudioManager.AUDIOFOCUS_GAIN:
                // resume playback
                if (mediaPlayer == null) load(); // Si le mediaPlayer n'est pas configuré, on le configure
                else if (!mediaPlayer.isPlaying()) mediaPlayer.start(); // Sinon on reprend où on en était
                mediaPlayer.setVolume(1.0f, 1.0f); // On remet le volume à 100%
                break;
 
            case AudioManager.AUDIOFOCUS_LOSS:
                // Lost focus for an unbounded amount of time: stop playback and release media player
                if (mediaPlayer == null) break; // Déjà libéré, ne rien faire
                if (mediaPlayer.isPlaying()) mediaPlayer.stop(); // On arrête la musique en cours
                release();
                break;
 
            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                // Lost focus for a short time, but we have to stop
                // playback. We don't release the media player because playback
                // is likely to resume
                if (mediaPlayer == null) break; 
                if (mediaPlayer.isPlaying()) mediaPlayer.pause();
                break;
 
            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                // Lost focus for a short time, but it's ok to keep playing
                // at an attenuated level
                if (mediaPlayer == null) break; 
                if (mediaPlayer.isPlaying()) mediaPlayer.setVolume(0.1f, 0.1f);
                break;
        }
    }
 
    public void play() {
        if (mediaPlayer == null) load();
        if (mediaPlayer != null) mediaPlayer.start();
    }
 
    public void stop() {
        mediaPlayer.stop();
        mediaPlayer.release();
        mediaPlayer=null;
    }
 
    public void pause() {
        mediaPlayer.pause();
    }
}

Il faut aussi demander l’accès aux fichiers privés (ici les sons disponibles sur votre téléphone ou tablette). Cela se fait dans le manifest.xml
juste avant la balise application, en ajoutant la ligne suivante :

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Obtenir une instance d’AudioPlayer

Au lancement de votre activité (après le setContentView() ), il faut récupérer l’accès à l’AudioPlayer.

Pour cela, ajoutez la ligne suivante :

audioPlayer = AudioPlayer.get(this);

Android studio signale que la variable audioPlayer n’existe pas, ajoutez-la comme un champ (on s’en servira dans d’autres méthodes).

Obtenir la liste des morceaux disponibles sur votre téléphone

Les fonctions standard disponibles sous Android permettent de récupérer un Cursor avec la liste des morceaux disponibles. Si vous avez suivi le TP sur les bases de données, vous avez déjà rencontré la classe Cursor qui sert aussi à récupérer le résultat d’une requête SQLite (NB : vous pourrez probablement modifier ce TP pour afficher la liste les morceaux, au lieu d’en choisir un au hasard)

Voici la fonction selectAudioFile() à ajouter dans votre activité et à appeler à la fin du onCreate() :

private void selectAudioFile() {
        //On demande la liste des fichiers audios disponibles
        ContentResolver contentResolver = getContentResolver();
        Uri uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
        Cursor cursor = contentResolver.query(uri, null, null, null, null);
        if (cursor == null) {
            // query failed, handle error.
        } else if (!cursor.moveToFirst()) {
            // no media on the device
        } else {
 
            int ndx = (int) (cursor.getCount()*Math.random());
            cursor.move(ndx);
 
            int titleColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE);
            int idColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID);
 
            long thisId = cursor.getLong(idColumn);
            String thisTitle = cursor.getString(titleColumn);
 
            ((TextView) findViewById(R.id.textView)).setText(thisTitle);
 
            Uri contentUri = ContentUris.withAppendedId(
                    android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, thisId);
 
            audioPlayer.loadUri(this, contentUri);
        }
    }

Configurer les boutons

Il faut configurer les trois boutons dans le onCreate() :

//Configuration des actions des boutons
btnPlay = (Button) findViewById(R.id.buttonPlay);
btnPlay.setEnabled(true);
btnPlay.setOnClickListener(this);
 
btnStop = (Button) findViewById(R.id.buttonStop);
btnStop.setEnabled(false);
btnStop.setOnClickListener(this);
 
findViewById(R.id.buttonNew).setOnClickListener(this);

Comme d’habitude, ajoutez les variables non déclarées comme champs, et ajoutez l’écouteur pour le clic sur les boutons.
Pour l’écouteur, on peut utiliser le code suivant :

@Override
    public void onClick(View view) {
        if (audioPlayer == null){
            audioPlayer = AudioPlayer.get(this);
            if (audioPlayer == null) return;
        }
 
        if (view.getId() == R.id.buttonNew){
            selectAudioFile();
            btnPlay.setEnabled(true);
            btnStop.setEnabled(false);
        } else if (view.getId() == R.id.buttonPlay) {
            audioPlayer.play();
            btnPlay.setEnabled(false);
            btnStop.setEnabled(true);
        } else {
            audioPlayer.pause();
            btnPlay.setEnabled(true);
            btnStop.setEnabled(false);
        }
    }

A noter : l’appel à setEnable() pour modifier l’état (actif ou non) des boutons.

L’application devrait maintenant être fonctionnelle. Mais il reste quelques améliorations à effectuer.

Gestion de la fin de l’activité

En plus des resources utilisées à libérer, il convient de stopper la musique quand on quitte l’activité (un player en tache de fond sera l’objet d’un prochain TP).
Pour cela, il faut gérer l’évènement stop de l’activité :

@Override
    protected void onStop() {
        super.onStop();
        AudioPlayer.release();
    }

Gestion de la fin du morceau

Pour détecter la fin du morceau, il faut demander à écouter les évènement. Dans le onCreate(), après avoir obtenu l’audioPlayer, on ajoute :

audioPlayer.setOnCompletionlistener(this);

On fait ensuite ajouter l’écouteur.

Dans l’écouteur, on met à jour les boutons :

btnPlay.setEnabled(true);
btnStop.setEnabled(false);

Si on veut automatiquement relancer ce morceau ou un autre, c’est aussi là qu’il faudrait ajouter le code correspondant.

Références

La documentation de MediaPlayer (JavaDoc) et (guide sur Android Developer)

En complément, le TP sur les bruitages TP Android : Les sons (bruitages)
Pour aller plus loin, la musique en tache de fond : TP Android : Les sons (musique en tache de fond)

Programmation Android

TP Android : Les sons (bruitages)

Ce TP explique comment gérer les sons, par exemple pour un jeu. Seuls les sons courts et multiples (bruitages) sont traités dans ce TP. Les sons de type musique feront l’objet d’un autre TP.

Bruitages (multiples sons courts)

Mise en place

Il vous faut :

  • Une activité avec un un bouton (on supposera qu’il a l’identifiant @id/button par la suite)
  • Des sons courts (quelques secondes maximum), au format mp3 ou ogg, placés dans le répertoire res/raw (on utilisera @raw/sound comme identifiant dans ce TP)

Préparation

Pour gérer nos sons, nous allons utiliser la classe SoundPool. Avec cette classe, on peut pré-charger des sons et les jouer simplement à la demande.
Dans la méthode onCreate, on va créer et configurer une instance de SoundPool, juste après le setContentView() :

        soundPool = new SoundPool(5, AudioManager.STREAM_MUSIC,0);
        soundId = soundPool.load(this, R.raw.sound, 0);

Android studio se plaindra que les variables soundPool et soundId ne sont pas déclarées, il suffit de les ajouter comme champs (on s’en servira dans d’autres méthodes)

Dans le constructeur de SoundPool, les paramètres sont :

  • Le nombre de sons à jouer simultanément (ici, 5)
  • Le type de flux, qui sera toujours STREAM_MUSIC, sauf si vous écrivez une alarme.
  • La qualité sonore (qui n’est pas encore prise en compte)

La méthode load() charge un son à partir de notre ressource (dans le répertoire raw). L’identifiant retourné servira à identifier le son quand on souhaitera le jouer.

Jouer le son

Pour jouer le son à chaque fois que l’on appuie sur le bouton, il suffit de placer la ligne suivante dans le onClick, et de s’assurer qu’il sera bien appelé lors d’un clic :

soundPool.play(soundId, 1f, 1f, 0, 0, 1f);

Les paramètres sont :

  • soundId : l’identifiant du son retourné par load()
  • 1f,1f : le volume à gauche et à droite (ici 1f = 100%)
  • 0 : La priorité
  • 0 : Le nombre de répétitions (en plus de la première fois)
  • 1f : la vitesse, entre 0.5f et 2f (1f = 100%)

Rappel : pour écouter les clics sur le bouton, on peut :

  • configurer le champ onClic= »"
  • après le setContentView, récupérer l’objet button pour appeler setOnClickListerner(this) dessus et implémenter la méthode onClick()

On peut aussi faire un setOnClickListener() avec un objet servant uniquement à définir notre action :

Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                soundPool.play(soundId, 1f, 1f, 0, 0, 1f);
            }
        });

Note : une classe utilisée directement pour instancier un objet, en héritant d’une classe mais sans nommer la nouvelle classe (comme ici) est appelée classe anonyme. Cela permet d’éviter de créer un fichier pour la classe (ou une deuxième classe — interne — dans notre classe actuelle) et évite de surcharger le code. On utilise généralement les classe anonymes pour fournir un paramètre de type écouteur, quand l’action à réaliser est trop simple pour justifier de définir une nouvelle classe.

Libérer les resources

La classe soundPool utilise beaucoup de resources : les fichiers sons chargés en mémoire ne sont pas compressés du tout, afin d’être jouables immédiatement. De plus, les voix (et tous les buffers correspondants) demandées sont elles aussi mobilisées. Or, même quand l’application n’est plus visible, ces ressources continuent à être utilisées, à moins d’être explicitement libérées.

Pour rendre ces ressources disponibles pour d’autres applications, il faut détecter la fin de notre activité. Pour cela, on utilise la méthode onStop(). Pour l’ajouter, utilisez le menu contextuel (generate–override–onStop). Après le « super.onStop() » ajoutez les lignes suivantes :

        soundPool.release();
        soundPool = null;

Il faut ensuite s’assurer que si on revient sur l’application, les sons seront à nouveau disponibles. C’est la méthode onRestart() qu’il faut cette fois-ci redéfinir (override). Comme on va utiliser le même code que dans onCreate, on place d’abord celui-ci dans une méthode initSound() : sélectionner les deux lignes initialisant soundPool et soundId, et utilisez le menu contextuel pour créer la méthode (refactor–extract– method). Appelez ensuite initSound() dans le onRestart().

Vous devriez obtenir le code suivant :

    @Override
    protected void onRestart() {
        super.onRestart();
        initSound();
    }

Références

La documentation de SoundPool (en particulier la description de la classe, qui pose bien les problèmes)(JavaDoc)

En complément, le TP sur la musique TP Android : Les sons (musique)
Et pour jouer la musique en dehors de l’activité : TP Android : Les sons (musique en tache de fond)