PDM Avanzado Reprodución de Audio
Sumario
Introdución
No caso da reprodución do audio, teremos que facer uso das clases MediaPlayer e AudioManager.
Clase utilizadas: MediaPlayer: esta clase é a principal utilizada para reproducir audio – vídeo. AudioManager: manexa fontes de audio e gravacións de audio en dispositivos. Nesta parte só imos utilizala para indicar o tipo de audio.
Permisos necesarios a engadir no arquivo AndroidManifest.xml (nos imos engadir todos):
- Se o dispositivo necesita conexión a internet (para escoitar música en streaming, por exemplo) temos que engadir o permiso:
1 <uses-permission android:name="android.permission.INTERNET" />
- Permiso Wake-Lock: no momento no que o S.O. non vexa ‘movemento’ no dispositivo (ou sexa uso) pasará a un estado de modo suspendido. Se queremos que a nosa activity non entre en dito estado podemos facer uso do método MediaPlayer.setWakeMode() e polo tanto necesitaremos o permiso wake_lock:
1 <uses-permission android:name="android.permission.WAKE_LOCK" />
- Permiso de escritura na SD Card: Para ter acceso á memoria externa teremos que especificar no arquivo AndroidManifest.xml que a nosa aplicación necesita permiso de lectura/escritura en dicha memoria.
- Se imos ler na tarxeta SD:
- Se a versión do S.O. Android é inferior á 4.1 non precisamos ningún permiso.
- Se a versión do S.O. Android é superior ou igual á 4.1 debemos engadir o permiso: <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
- Se imos escribir na tarxeta SD:
- Se a versión do S.O. Android é inferior á 4.4 o permiso é: <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> .
- Se a versión do S.O. Android é a 4.4 ou superior. Podemos poñer o mesmo permiso anterior pero as aplicacións dispoñen dun cartafol para escribir na SD (cartafol Android/data/paquete/) sen necesidade de ter o permiso anterior.
1 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Nota: se queremos ter unha música de fondo na nosa aplicación o máis lóxico sería ter un servizo.
Para engadir ditos permisos de forma gráfica, nos abrimos o arquivo AndroidManifest.xml.
- Proceso para engadir permisos de forma gráfica
- PDM Avanzada Multimedia Reprod 01.jpg
Prememos a pestana ‘Permissions’ e prememos o botón Add.
- PDM Avanzada Multimedia Reprod 02.jpg
Escollemos a opción Uses Permission.
- PDM Avanzada Multimedia Reprod 03.jpg
Graficamente escollemos os permisos que necesitemos.
MediaPlayer
Os recursos (arquivos de audio) que podemos utilizar con esta clase poden ser:
- Locais: arquivos locais gardados no cartafol /res/raw/ (deberemos crear o cartafol /raw/ se non existe).
- URIs internas: unha URI é unha forma de identificar de forma unívoca un recurso (neste caso multimedia). Podemos referenciar arquivos multimedia gardados nas diferentes tarxetas de memoria do dispositivo.
- URL Externos (streaming): arquivos multimedia que se atopan en Internet.
Podedes consultar a lista de formatos de audio – vídeo soportados en http://developer.android.com/guide/appendix/media-formats.html
Imos facer unha pequena práctica na que imos reproducir un arquivo multimedia.
Creamos no proxecto de Android - Multimedia un cartafol de nome ‘raw’ dentro do cartafol /res/ do proxecto:
Reproducir música
Para cargar un arquivo de audio temos que seguir os seguintes pasos:
- Crear un obxecto da clase MediaPlayer.
1 private MediaPlayer mediaplayer;
2 .............
3 mediaplayer = new MediaPlayer();
- Chamar ao método setDataSource que queiramos (pode dar lugar a varios tipos de excepcións, polo que teremos que usar try catch para cada tipo delas ou ben capturalos cunha clase Exception aínda que esta forma non é a recomendada).
- No exemplo usamos Exception para simplificar o código xa que se non teríamos que ter isto:
1 try {
2 mediaplayer.setDataSource(path);
3 } catch (IllegalArgumentException e) {
4 // TODO Auto-generated catch block
5 e.printStackTrace();
6 } catch (SecurityException e) {
7 // TODO Auto-generated catch block
8 e.printStackTrace();
9 } catch (IllegalStateException e) {
10 // TODO Auto-generated catch block
11 e.printStackTrace();
12 } catch (IOException e) {
13 // TODO Auto-generated catch block
14 e.printStackTrace();
15 }
Con este método indicamos de onde imos a cargar o audio.
Podemos ter varias posibilidades:
- O arquivo se atopa en /res/raw:
1 MediaPlayer mediaplayer = new MediaPlayer(); 2 Uri uri = Uri.parse("android.resource://" + getPackageName()+ "/" + R.raw.snowflake_persephone); 3 try { 4 mediaplayer.setDataSource(getApplicationContext(), uri); 5 ..... 6 }
- O arquivo provén da tarxeta SD.
- Temos varias opcións:
1 MediaPlayer mediaplayer = new MediaPlayer(); 2 String path=Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator + "snowflake_persephone.mp3"; 3 try { 4 mediaplayer.setDataSource(path); 5 ..... 6 }
1 MediaPlayer mediaplayer = new MediaPlayer(); 2 String path =Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator + "snowflake_persephone.mp3"; 3 Uri uri = Uri.parse(Uri.encode(path)); 4 try { 5 mediaplayer.setDataSource(getApplicationContext(),uri); 6 ..... 7 }
- O arquivo provén de Internet (música en Streaming).
Nota: Se estades a utilizar un dispositivo real ten que ter conexión a Internet para que funcione.
- Temos varias opcións:
1 MediaPlayer mediaplayer = new MediaPlayer(); 2 String url="http://www.mfiles.co.uk/mp3-downloads/edvard-grieg-peer-gynt1-morning-mood.mp3"; 3 try { 4 mediaplayer.setDataSource(url); 5 ..... 6 }
- Temos varias opcións:
Pode suceder que a dirección da URL teña espazos en branco ou caracteres especiais. Nese caso teremos que 'codificar' antes a url a partires do último carácter '/':
1 MediaPlayer mediaplayer = new MediaPlayer(); 2 String url="http://www.mfiles.co.uk/mp3-downloads/edvard-grieg-peer-gynt1-morning-mood.mp3"; 3 int pos = url.lastIndexOf('/') + 1; 4 Uri uri = Uri.parse(url.substring(0, pos) + Uri.encode(url.substring(pos))); 5 try { 6 mediaplayer.setDataSource(getApplicationContext(),uri); 7 ..... 8 }
- Chamar ao método setAudioStreamType(AudioManager.STREAM_MUSIC) para indicarlle que o que imos reproducir será música e poñerá o volume que teña o S.O. (lembrar que en Android podemos cambiar o volume da música, notificacións e alarmas).
- Chamar ao método prepare (pode lanzar unha excepción IOException).
- Chamar ao método start.
Todos estes pasos son necesarios facelos nesta orde xa que o MediaPlayer vai pasar por unha serie de estados que nos van obrigar a poder facer só unha accións determinadas dependendo do estado no que nos atopemos.
Vexamos o diagrama de estados do MediaPlayer:
Imos analizar dito diagrama.
Cando instanciamos o MediaPlayer (facemos o new) nos situamos no estado idle. Dende dito estado so podemos pasar o estado initialized chamando o método setDataSource.
Se intentemos chamar a outro método (coma start(),...) daranos unha excepción (IlegalStateException). Cando estamos a tocar unha canción non podemos cambiar por outra, xa que aínda que chamemos o método stop e pasemos o estado de stop, dende dito estado non podemos chamar ao método setDataSource (mirar diagrama).
A única forma de cambiar de canción será chamando ao método reset(), que volve ao estado Idle como amosamos neste anaco do diagrama anterior:
Podemos cargar un arquivo de audio sen facer os pasos anteriores da seguinte forma:
1 MediaPlayer mediaPlayer=MediaPlayer.create(this, R.raw.snowflake_persephone);
2 mediaPlayer.start();
Sendo R.raw.snowflake_persephone un arquivo de audio que se atopa no cartafol /res/raw.
Desta forma estamos a pasar directamente ao estado Prepared do diagrama anterior. No nosa práctica non usaremos esta forma de cargar o audio.
Caso práctico
O obxectivo desta práctica é reproducir un arquivo de música gardado en /res/raw e outro en streaming de Internet.
Nota: Se instalades a aplicación nun dispositivo real tedes que ter conexión a Internet para que funcione a reprodución en streaming.
Preparación
- Copiamos o arquivo de audio (ou outro calquera que podades utilizar) ao cartafol /res/raw.
Media:snowflake_persephone.mp3 .
ATENCIÓN: Ao ser un arquivo que vai ser gardado no interior do cartafol /res de Android, o arquivo non pode ter letras maiúsculas.
- Copiando arquivo de audio a /res/raw
Creamos a Activity
- Nome do proxecto: UD6_01_MultimediaReproductor
- Nome da activity: UD6_01_MultimediaReproductor.java
Código do layout xml
Nota: Por motivos de tempo para o alumnado o deseño non fai uso de constantes externas definidas no cartafol values. Queda claro que esta debería ser a opción escollida para o deseño das Interfaces de Usuario.
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:tools="http://schemas.android.com/tools"
3 android:id="@+id/LinearLayout1"
4 android:layout_width="match_parent"
5 android:layout_height="wrap_content"
6 android:orientation="vertical" >
7
8
9 <TextView
10 android:layout_width="match_parent"
11 android:layout_height="wrap_content"
12 android:gravity="center_horizontal"
13 android:layout_marginBottom="20dp"
14 android:text="UD6_01_Multimedia"
15 android:textSize="20sp" />
16
17
18 <TableLayout
19 android:layout_width="match_parent"
20 android:layout_height="wrap_content"
21 android:layout_gravity="bottom"
22 android:isScrollContainer="true"
23 >
24
25 <TableRow
26 android:id="@+id/tableRow1"
27 android:layout_width="match_parent"
28 android:layout_height="wrap_content" >
29
30
31 <Button
32 android:id="@+id/UD6_01_btnReprLocal"
33 android:width="0dp"
34 android:layout_weight="1"
35 android:layout_width="wrap_content"
36 android:layout_height="wrap_content"
37 android:text="LOCAL"
38 android:textSize="10sp"
39 />
40
41 <Button
42 android:id="@+id/UD6_01_btnReprInternet"
43 android:width="0dp"
44 android:layout_weight="1"
45 android:layout_width="wrap_content"
46 android:layout_height="wrap_content"
47 android:text="INTERNET"
48 android:textSize="10sp"
49 />
50
51 </TableRow>
52
53 <TableRow
54 android:id="@+id/tableRow2"
55 android:layout_width="match_parent"
56 android:layout_height="wrap_content" >
57
58 <Button
59 android:id="@+id/UD6_02_btnPararRepr"
60 android:layout_width="0dp"
61 android:layout_height="wrap_content"
62 android:layout_weight="1"
63 android:text="PARAR"
64 android:textSize="10sp" />
65
66 </TableRow>
67 <TableRow
68 android:id="@+id/tableRow4"
69 android:layout_width="match_parent"
70 android:layout_height="wrap_content" >
71
72 <Button
73 android:id="@+id/UD6_02_btnSairAplicacion"
74 android:layout_width="0dp"
75 android:layout_height="wrap_content"
76 android:layout_weight="1"
77 android:text="SAIR"
78 android:textSize="10sp"
79 />
80
81 </TableRow>
82
83
84 </TableLayout>
85
86 </LinearLayout>
Código da clase UD6_01_MultimediaReproductor
Obxectivo: Reproducir e parar música local e de Internet.
1 package com.platega.angel.multimedia;
2
3 import java.io.File;
4
5 import android.app.Activity;
6 import android.media.AudioManager;
7 import android.media.MediaPlayer;
8 import android.net.Uri;
9 import android.os.Bundle;
10 import android.os.Environment;
11 import android.util.Log;
12 import android.view.View;
13 import android.view.View.OnClickListener;
14 import android.widget.Button;
15
16 public class UD6_01_MultimediaReproductor extends Activity {
17
18 private MediaPlayer mediaplayer;
19 private boolean pause; // Indica se o mediaplayer estaba tocando cando cambiamos de aplicación
20
21
22 /**
23 * Cambia a canción no MediaPlayer
24 * @param uri
25 */
26 private void cambiarCancion(Uri uri){
27 try {
28 mediaplayer.reset();
29
30 mediaplayer.setDataSource(getApplicationContext(),uri);
31 mediaplayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
32 mediaplayer.prepare();
33 mediaplayer.start();
34 } catch (Exception e) {
35 // TODO Auto-generated catch block
36 e.printStackTrace();
37 Log.e("MULTIMEDIA",e.getMessage());
38 }
39
40 }
41
42 private void xestionarEventos(){
43
44 Button btnLocal = (Button)findViewById(R.id.UD6_01_btnReprLocal);
45 btnLocal.setOnClickListener(new OnClickListener() {
46
47 @Override
48 public void onClick(View v) {
49 // TODO Auto-generated method stub
50 Uri uri = Uri.parse("android.resource://" + getPackageName()+ "/" + R.raw.snowflake_persephone);
51 cambiarCancion(uri);
52 }
53 });
54
55 Button btnInternet = (Button)findViewById(R.id.UD6_01_btnReprInternet);
56 btnInternet.setOnClickListener(new OnClickListener() {
57
58 @Override
59 public void onClick(View v) {
60 // TODO Auto-generated method stub
61 String url = "http://www.mfiles.co.uk/mp3-downloads/edvard-grieg-peer-gynt1-morning-mood.mp3";
62 Uri uri = Uri.parse(Uri.encode(url));
63 cambiarCancion(uri);
64 }
65 });
66
67 Button btnParar = (Button)findViewById(R.id.UD6_02_btnPararRepr);
68 btnParar.setOnClickListener(new OnClickListener() {
69
70 @Override
71 public void onClick(View v) {
72 // TODO Auto-generated method stub
73 if (mediaplayer.isPlaying())
74 mediaplayer.stop();
75 pause=false;
76 }
77 });
78
79 Button btnSair = (Button) findViewById(R.id.UD6_02_btnSairAplicacion);
80 btnSair.setOnClickListener(new OnClickListener() {
81
82 @Override
83 public void onClick(View v) {
84 // TODO Auto-generated method stub
85 finish();
86 }
87 });
88
89
90 }
91
92 @Override
93 protected void onPause() {
94 super.onPause();
95
96 if (mediaplayer.isPlaying()){
97 mediaplayer.pause();
98 pause = true;
99 }
100 }
101
102 @Override
103 protected void onResume() {
104 super.onResume();
105
106 if (pause) {
107 mediaplayer.start();
108 pause = false;
109 }
110 }
111
112
113 @Override
114 protected void onSaveInstanceState(Bundle estado) {
115 estado.putBoolean("MEDIAPLAYER_PAUSE", pause);
116 super.onSaveInstanceState(estado);
117 }
118
119 @Override
120 protected void onRestoreInstanceState(Bundle savedInstanceState) {
121 super.onRestoreInstanceState(savedInstanceState);
122 savedInstanceState.putBoolean("MEDIAPLAYER_PAUSE", false);
123 pause = savedInstanceState.getBoolean("MEDIAPLAYER_PAUSE");
124 }
125
126 @Override
127 protected void onDestroy() {
128 super.onDestroy();
129
130 if (mediaplayer.isPlaying()) mediaplayer.stop();
131
132 if (mediaplayer != null) mediaplayer.release();
133 mediaplayer = null;
134
135 }
136
137 @Override
138 protected void onCreate(Bundle savedInstanceState) {
139 super.onCreate(savedInstanceState);
140 setContentView(R.layout.activity_UD6_01__multimedia_reproductor);
141
142 mediaplayer = new MediaPlayer();
143 pause = false;
144 xestionarEventos();
145
146
147 }
148 }
- Liña 18: Definimos o reprodutor.
- Liña 19: Definimos unha variable booleana que nos indique cando o reprodutor está en estado de PAUSE.
- Liñas 26-40: Cambiamos de canción. Fixarse como sempre chamamos ao método reset() para pasar ao estado que nos permite cambiar a fonte de audio.
- Liñas 48-52: Prememos o botón de LOCAL e definimos a URI para buscar o arquivo en /res/raw.
- Liñas 59-64: Prememos o botón de INTERNET e definimos a URI para buscar o arquivo indicada na URL.
- Liñas 71-76: Prememos o botón de STOP. Comprobamos se o mediaplayer está tocando para paralo.
- Liñas 106-109: Comprobamos se o mediaplayer está tocando para poñelo en pause. Cambiamos o valor da variable pause a true. Isto é necesario xa que non temos forma de saber a través do mediaplayer se este se atopa nese estado.
- Liñas 106-109: Se volvemos á aplicación comprobamos se o mediaplayer estaba tocando (pause=true) e nese caso continuamos tocando a canción. Isto pasará cando cambiemos de aplicación sen pechala.
- Liñas 113-124: Se cambiamos de aplicación recuperamos o estado do mediaplayer. Despois poñemos a false a variable pause por se xiramos o dispositivo estando tocando, para que non recupere o último estado.
- Liñas 126-135: De saímos da aplicación paramos de tocar e liberamos o mediaplayer.
- Liña 142: Instanciamos o mediaplayer.
Carga asíncrona
Cando poñemos en marcha un arquivo de audio que non se atopa localmente, non é boa idea facelo no fío principal da aplicación, xa que se leva un tempo cargar a música, e a aplicación quedaría bloqueada mentres tanto. Para evitalo teríamos que crear nós un fío de execución separado do principal, pero este traballo xa o temos feito, se chamamos ao método prepareAsync() . Cando a mediaplayer estea listo, chamará automaticamente ao método onPrepared da interface MediaPlayer.OnPreparedListener. Para asociar dita interface o mediaplayer, faremos uso do método setOnPreparedListener().
Os pasos serían:
- Asociar a interface ao mediaplayer:
1 mediaplayer.setOnPreparedListener(new OnPreparedListener(){
2
3 }
4 );
- Ao definir internamente a clase que xestionará o preparedlistener, é necesario implementar o método da interface dentro da definición:
1 mediaplayer.setOnPreparedListener(new OnPreparedListener(){
2
3 public void onPrepared(MediaPlayer arg0) {
4 // TODO Auto-generated method stub
5
6 }
7 }
8 );
- Agora xa podemos codificar o método onPrepared que será chamado automaticamente cando o música estea preparada. O único que temos que facer dentro de dito método será unha chamada ao método start() da clase MediaPlayer.
Nota: Agora en vez de chamar o método prepare, teremos que chamar o método prepareAsync().
Caso práctico
O obxectivo desta práctica é facer o mesmo que na práctica anterior pero chamando ao método prepareAsync.
Preparación
O alumno debe crear unha nova activity de nome UD6_02_MultimediaReproductor.
Esta activity ten o mesmo layout que a activity anterior (activity_UD6_01__multimedia_reproductor.xml) xa que non imos facer modificacións no seu aspecto.
Creamos a activity
- Nome do proxecto: UD6_02_MultimediaReproductor
- Nome da activity: UD6_02_MultimediaReproductor.java
Código da clase UD6_02_MultimediaReproductor
Obxectivo: Amosar como cargar unha canción de forma asíncrona.
1 package com.platega.angel.multimedia;
2
3 import android.app.Activity;
4 import android.media.AudioManager;
5 import android.media.MediaPlayer;
6 import android.media.MediaPlayer.OnPreparedListener;
7 import android.net.Uri;
8 import android.os.Bundle;
9 import android.util.Log;
10 import android.view.View;
11 import android.view.View.OnClickListener;
12 import android.widget.Button;
13
14 public class UD6_02_MultimediaReproductor extends Activity {
15
16 private MediaPlayer mediaplayer;
17 private boolean pause; // Indica se o mediaplayer estaba tocando cando cambiamos de aplicación
18
19
20 /**
21 * Cambia a canción no MediaPlayer
22 * @param uri
23 */
24 private void cambiarCancion(Uri uri){
25 try {
26 mediaplayer.reset();
27
28 mediaplayer.setDataSource(getApplicationContext(),uri);
29 mediaplayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
30 mediaplayer.prepareAsync();
31 // mediaplayer.start(); O FACEMOS DE FORMA ASINCRONA
32 } catch (Exception e) {
33 // TODO Auto-generated catch block
34 e.printStackTrace();
35 Log.e("MULTIMEDIA",e.getMessage());
36 }
37
38 }
39
40 private void xestionarEventos(){
41
42 mediaplayer.setOnPreparedListener(new OnPreparedListener() {
43
44 @Override
45 public void onPrepared(MediaPlayer mp) {
46 // TODO Auto-generated method stub
47 mediaplayer.start();
48 }
49 });
50
51 Button btnLocal = (Button)findViewById(R.id.UD6_01_btnReprLocal);
52 btnLocal.setOnClickListener(new OnClickListener() {
53
54 @Override
55 public void onClick(View v) {
56 // TODO Auto-generated method stub
57 Uri uri = Uri.parse("android.resource://" + getPackageName()+ "/" + R.raw.snowflake_persephone);
58 cambiarCancion(uri);
59 }
60 });
61
62 Button btnInternet = (Button)findViewById(R.id.UD6_01_btnReprInternet);
63 btnInternet.setOnClickListener(new OnClickListener() {
64
65 @Override
66 public void onClick(View v) {
67 // TODO Auto-generated method stub
68 String url = Uri.encode("http://www.mfiles.co.uk/mp3-downloads/edvard-grieg-peer-gynt1-morning-mood.mp3");
69 Uri uri = Uri.parse(Uri.encode(url));
70 cambiarCancion(uri);
71 }
72 });
73
74 Button btnParar = (Button)findViewById(R.id.UD6_02_btnPararRepr);
75 btnParar.setOnClickListener(new OnClickListener() {
76
77 @Override
78 public void onClick(View v) {
79 // TODO Auto-generated method stub
80 if (mediaplayer.isPlaying())
81 mediaplayer.stop();
82 pause=false;
83 }
84 });
85
86 Button btnSair = (Button) findViewById(R.id.UD6_02_btnSairAplicacion);
87 btnSair.setOnClickListener(new OnClickListener() {
88
89 @Override
90 public void onClick(View v) {
91 // TODO Auto-generated method stub
92 finish();
93 }
94 });
95
96
97 }
98
99 @Override
100 protected void onPause() {
101 super.onPause();
102
103 if (mediaplayer.isPlaying()){
104 mediaplayer.pause();
105 pause = true;
106 }
107 }
108
109 @Override
110 protected void onResume() {
111 super.onResume();
112
113 if (pause) {
114 mediaplayer.start();
115 pause = false;
116 }
117 }
118
119
120 @Override
121 protected void onSaveInstanceState(Bundle estado) {
122 estado.putBoolean("MEDIAPLAYER_PAUSE", pause);
123 super.onSaveInstanceState(estado);
124 }
125
126 @Override
127 protected void onRestoreInstanceState(Bundle savedInstanceState) {
128 super.onRestoreInstanceState(savedInstanceState);
129 savedInstanceState.putBoolean("MEDIAPLAYER_PAUSE", false);
130 pause = savedInstanceState.getBoolean("MEDIAPLAYER_PAUSE");
131 }
132
133 @Override
134 protected void onDestroy() {
135 super.onDestroy();
136
137 if (mediaplayer.isPlaying()) mediaplayer.stop();
138
139 if (mediaplayer != null) mediaplayer.release();
140 mediaplayer = null;
141
142 }
143
144 @Override
145 protected void onCreate(Bundle savedInstanceState) {
146 super.onCreate(savedInstanceState);
147 setContentView(R.layout.activity_UD6_02__multimedia_reproductor);
148
149 mediaplayer = new MediaPlayer();
150 pause = false;
151 xestionarEventos();
152
153
154 }
155 }
-- Ángel D. Fernández González e Carlos Carrión Álvarez -- (2014).
