8.4: Il rilascio delle risorse multimediali e altri strumenti per la loro gestione in Android

Condividi su:

Nell’articolo precedente abbiamo messo su un banalissimo player che si occupa di eseguire un file fornito come risorsa raw. Ci siamo lasciati dicendo che era presente una criticità. In questo articolo vedremo come risolverla e illustreremo qualche altro strumento.





I file multimediali impiegano parecchie risorse. Queste vengono impegnate nel momento in cui viene richiamato il metodo “create()”. Se queste risorse non vengono rilasciate, quando l’app viene ad esempio terminata, si verificherebbero conseguenze non piacevoli, sopratutto per la user experience.

Liberare le risorse impegnate

La classe MediaPlayer, così come fornisce il metodo “create()”, rende disponibile un apposito metodo che fa esattamente l’opposto, cioè rilasciare tutte le risorse. Questo metodo andrebbe richiamato quando siamo certi che l’utente non avrà più bisogno del player. Volendo fare le cose per bene, deallocheremo anche l’oggetto MediaPlayer. Osservando il ciclo di vita di un’activity, il posto migliore per far tutto ciò sarà alla distruzione di questa. Quindi non ci resta da fare altro che l’override di “onDestroy()” e dare le istruzioni necessarie al rilascio delle risorse e alla distruzione dell’oggetto:

public class MainActivity extends AppCompatActivity {
    private MediaPlayer mediaPlayer;
    private Button playButton;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mediaPlayer = new MediaPlayer();
        mediaPlayer = MediaPlayer.create(getApplicationContext(),R.raw.the_dab_is_dead);

        playButton = (Button) findViewById(R.id.playButtonID);
        playButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mediaPlayer.isPlaying()) {
                    pauseMusic();
                } else {
                    playMusic();
                }
            }
        });
    }

    @Override
    protected void onDestroy() {
        if (mediaPlayer != null && mediaPlayer.isPlaying()){
            mediaPlayer.stop();
            mediaPlayer.release();
            mediaPlayer = null;
        }
        super.onDestroy();
    }

    public void pauseMusic(){
        if (mediaPlayer != null){
            mediaPlayer.pause();
            playButton.setText(getResources().getString(R.string.play));
        }
    }

    public void playMusic(){
        if (mediaPlayer != null){
            mediaPlayer.start();
            playButton.setText(getResources().getString(R.string.pause));
        }
    }
}

Per prima cosa ci premuriamo dell’esistenza dell’oggetto e se il file è in riproduzione. Se si verificano queste condizioni, fermiamo la riproduzione, rilasciamo le risorse e distruggiamo l’oggetto. Ovviamente non abbiamo risolto tutte le criticità, provate a pensarci. Se il file non è in riproduzione, nel momento in cui viene terminata l’app, le risorse non verranno correttamente rilasciate. Lascio a voi la risoluzione del problema, che è anche molto banale.

La gestione degli eventi generati dal media player

La classe MediaPlayer, come già detto, fornisce una moltitudine di strumenti per la gestione della riproduzione di file o flussi multimediali, sarebbe impossibile osservarli tutti, oltre al fatto che già sono ampiamente documentati. Vedremo qualcos’altro a titolo di esempio, comunque non ci fermeremo qui, poiché col prossimo articolo inizieremo lo sviluppo di una vera e propria app, più elaborata di quelle riepilogative create finora.

Gli oggetti MediaPlayer arrivano a corredo con una serie di listener e quindi con gli opportuni strumenti per gestire gli eventi peculiari di questi. Il loro funzionamento e la loro implementazione è perfettamente equivalente agli eventi visti e gestiti finora. Si collega un listener all’oggetto di cui catturare un determinato evento attraverso un apposito setter e si demanda la gestione dell’evento ad un’altro oggetto predisposto per tale scopo.

Ad esempio possiamo settare un listener che monitorerà la fine della riproduzione:

public class MainActivity extends AppCompatActivity {
    private MediaPlayer mediaPlayer;
    private Button playButton;
    private SeekBar mSeekBar;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mediaPlayer = new MediaPlayer();
        mediaPlayer = MediaPlayer.create(getApplicationContext(),R.raw.the_dab_is_dead);
        mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mediaPlayer) {
                //codice da eseguire al termine della riproduzione del file audio
            }
        });

        playButton = (Button) findViewById(R.id.playButtonID);
        playButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mediaPlayer.isPlaying()) {
                    pauseMusic();
                } else {
                    playMusic();
                }
            }
        });


    }

    @Override
    protected void onDestroy() {
        if (mediaPlayer != null && mediaPlayer.isPlaying()){
            mediaPlayer.stop();
            mediaPlayer.release();
            mediaPlayer = null;
        }
        super.onDestroy();
    }

    public void pauseMusic(){
        if (mediaPlayer != null){
            mediaPlayer.pause();
            playButton.setText(getResources().getString(R.string.play));
        }
    }

    public void playMusic(){
        if (mediaPlayer != null){
            mediaPlayer.start();
            playButton.setText(getResources().getString(R.string.pause));
        }
    }
}

Come vedete è tutto perfettamente equivalente alla gestione degli eventi click, solo che in questo caso l’evento è generato dall’oggetto “mediaPlayer”; quindi settiamo su questo un listener che invoca la gestione dell’evento nel momento in cui la riproduzione è completata. Viene creato un apposito oggetto, in questo caso anonimo, per gestire l’evento e si fa il solito override del metodo che viene invocato al verificarsi dell’evento. All’interno di questo va inserito il codice da eseguire.

Ad esempio possiamo visualizzare un toast che indica la durata del file riprodotto, così possiamo vedere anche uno dei tanti getter messi a disposizione:

public class MainActivity extends AppCompatActivity {
    private MediaPlayer mediaPlayer;
    private Button playButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mediaPlayer = new MediaPlayer();
        mediaPlayer = MediaPlayer.create(getApplicationContext(),R.raw.the_dab_is_dead);
        mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mediaPlayer) {
                int duration = mediaPlayer.getDuration();
                String mDuration = String.valueOf(duration/1000);
                Toast.makeText(getApplicationContext(), (getResources().getString(R.string.duration)+mDuration), Toast.LENGTH_LONG).show();
            }
        });

        playButton = (Button) findViewById(R.id.playButtonID);
        playButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mediaPlayer.isPlaying()) {
                    pauseMusic();
                } else {
                    playMusic();
                }
            }
        });


    }

    @Override
    protected void onDestroy() {
        if (mediaPlayer != null && mediaPlayer.isPlaying()){
            mediaPlayer.stop();
            mediaPlayer.release();
            mediaPlayer = null;
        }
        super.onDestroy();
    }

    public void pauseMusic(){
        if (mediaPlayer != null){
            mediaPlayer.pause();
            playButton.setText(getResources().getString(R.string.play));
        }
    }

    public void playMusic(){
        if (mediaPlayer != null){
            mediaPlayer.start();
            playButton.setText(getResources().getString(R.string.pause));
        }
    }
}

Abbiamo semplicemente dichiarato una variabile intera, all’interno di questa abbiamo memorizzato la durata del file (in millisecondi, che è l’unità di misura del tempo predefinita della piattaforma) sfruttando l’apposito getter. Successivamente abbiamo convertito questo valore in stringa, per poterlo utilizzare all’interno di un toast, premurandoci di convertirlo in secondi (semplicemente dividendolo per 1000). Fatto ciò abbiamo costruito e visualizzato il toast.
Provando l’app e aspettando che termini la riproduzione del file, noteremo che al verificarsi di questo evento, apparirà il toast.

Saltare all’interno della traccia

Se abbiamo caricato un file molto lungo e non vogliamo aspettare il termine per vedere se il codice predisposto funziona? Allora facciamo in modo da poter saltare all’interno della traccia, magari con l’aiuto di una SeekBar.

Questa andrà ovviamente inserita nel layout e successivamente referenziata all’interno del codice. Vogliamo fare in modo che muovendo il cursore, la riproduzione parta dal punto esatto che abbiamo scelto. Per ottenere ciò, per prima cosa dobbiamo impostare il valore massimo della SeekBar uguale alla durata del file, così ogni scostamento del cursore corrisponderà ad un determinato secondo (o meglio millisecondo) del file; poi settare un listener su questa e gestire l’evento. Per ovviare al primo problema, abbiamo sia il setter della SeekBar per l’impostazione del valore massimo, che il getter del media player per reperire la durata, tra l’altro già visto, quindi:

public class MainActivity extends AppCompatActivity {
    private MediaPlayer mediaPlayer;
    private Button playButton;
    private SeekBar mSeekBar;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mediaPlayer = new MediaPlayer();
        mediaPlayer = MediaPlayer.create(getApplicationContext(),R.raw.the_dab_is_dead);
        mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mediaPlayer) {
                int duration = mediaPlayer.getDuration();
                String mDuration = String.valueOf(duration/1000);
                Toast.makeText(getApplicationContext(), (getResources().getString(R.string.duration)+mDuration), Toast.LENGTH_LONG).show();
            }
        });

        mSeekBar = (SeekBar) findViewById(R.id.mSeekBarID);
        mSeekBar.setMax(mediaPlayer.getDuration());
        mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });

        playButton = (Button) findViewById(R.id.playButtonID);
        playButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mediaPlayer.isPlaying()) {
                    pauseMusic();
                } else {
                    playMusic();
                }
            }
        });


    }

    @Override
    protected void onDestroy() {
        if (mediaPlayer != null && mediaPlayer.isPlaying()){
            mediaPlayer.stop();
            mediaPlayer.release();
            mediaPlayer = null;
        }
        super.onDestroy();
    }

    public void pauseMusic(){
        if (mediaPlayer != null){
            mediaPlayer.pause();
            playButton.setText(getResources().getString(R.string.play));
        }
    }

    public void playMusic(){
        if (mediaPlayer != null){
            mediaPlayer.start();
            playButton.setText(getResources().getString(R.string.pause));
        }
    }
}

Volendo agire nel momento in cui viene spostato il cursore, il codice per saltare all’interno della traccia lo inseriremo all’interno del metodo “onProgressChanged()”.

Ci avvarremo del metodo “seekTo()”, facente parte ovviamente del corredo del media player, questo non fa altro che cercare il punto del file corrispondente al tempo, in millisecondi, passato come parametro intero, e riprendere la riproduzione da lì. Tra i parametri di “onProgressChanged()”, il secondo indica la posizione del cursore della progress bar e per il giochetto fatto prima (impostare la posizione massima uguale alla durata del file) questo corrisponde ad un tempo in millisecondi valido. Quindi passeremo come parametro a “seekTo()” proprio quel valore:

public class MainActivity extends AppCompatActivity {
    private MediaPlayer mediaPlayer;
    private Button playButton;
    private SeekBar mSeekBar;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mediaPlayer = new MediaPlayer();
        mediaPlayer = MediaPlayer.create(getApplicationContext(),R.raw.the_dab_is_dead);
        mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mediaPlayer) {
                int duration = mediaPlayer.getDuration();
                String mDuration = String.valueOf(duration/1000);
                Toast.makeText(getApplicationContext(), (getResources().getString(R.string.duration)+mDuration), Toast.LENGTH_LONG).show();
            }
        });

        mSeekBar = (SeekBar) findViewById(R.id.mSeekBarID);
        mSeekBar.setMax(mediaPlayer.getDuration());
        mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                if (fromUser){
                    mediaPlayer.seekTo(progress);
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });

        playButton = (Button) findViewById(R.id.playButtonID);
        playButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mediaPlayer.isPlaying()) {
                    pauseMusic();
                } else {
                    playMusic();
                }
            }
        });


    }

    @Override
    protected void onDestroy() {
        if (mediaPlayer != null && mediaPlayer.isPlaying()){
            mediaPlayer.stop();
            mediaPlayer.release();
            mediaPlayer = null;
        }
        super.onDestroy();
    }

    public void pauseMusic(){
        if (mediaPlayer != null){
            mediaPlayer.pause();
            playButton.setText(getResources().getString(R.string.play));
        }
    }

    public void playMusic(){
        if (mediaPlayer != null){
            mediaPlayer.start();
            playButton.setText(getResources().getString(R.string.pause));
        }
    }
}

Abbiamo terminato. Come avrete notato, ci siamo anche premurati di controllare che sia stato l’utente ad effettuare variazioni sul cursore, controllando il parametro predisposto prima di eseguire l’operazione di salto.



L’elenco di tutti i listener e quindi di tutti gli eventi gestibili lo trovate nella documentazione ufficiale, così come tutti gli altri dettagli e tutti gli altri strumenti disponibili. Abbiamo riportato qualche esempio per apprendere il meccanismo. Col prossimo articolo approfondiremo comunque la questione, creando un vero e proprio player e soffermandoci su tanti dettagli qui tralasciati. Aspettate i prossimi articoli, oppure esercitatevi apportando qualche modifica o provando altri metodi e simili, sfruttando anche gli spunti dati.

Condividi su:

label, , ,

About the author