Crea un Reproductor de Música en Android: Configuración del Proyecto
Spanish (Español) translation by Nadia Castelli (you can also view the original English article)
La plataforma Android provee recursos para manipular la reproducción de archivos multimedia, que pueden ser utilizados por tu app para crear una interfaz entre el usuario y sus archivos de música. En esta serie de tutoriales, crearemos un reproductor de música básico para Android. Nuestra app presentará una lista de canciones en el dispositivo del usuario, para que pueda seleccionarlas y reproducirlas. Además, la app tendrá controles para interactuar con la reproducción, la cual continuará cuando el usuario salga de la aplicación, mostrando una notificación mientras la reproducción continúe.
Introducción
El desarrollo del reproductor de música involucra el uso de la clase ContentResolver para obtener las pistas almacenadas en el dispositivo, la clase MediaPlayer para reproducir audio, y la clase MediaController para controlar la reproducción. También haremos uso de una instancia de Service para reproducir audio cuando el usuario no esté interactuando directamente con la app. Deberías ser capaz de completar esta serie si eres un desarrollador Android de nivel intermedio, por lo cual, si ya has desarrollado algunas aplicaciones, esta serie no debería resultarte un problema. Aquí tenemos una previsualización de la app final:

En este tutorial, crearemos la app y buscaremos pistas de audio en el dispositivo del usuario, utilizando las clases ContentResolver y Cursor. En la próxima parte, usaremos una instancia de Adapter para presentar las canciones en una lista, y comenzar la reproducción cuando el usuario toque un ítem de la lista. En la parte final de esta serie, utilizaremos la clase MediaController para darle control de la reproducción al usuario, implementar funciones para saltar la reproducción hacia adelante y hacia atrás, e incluir la función de reproducir de manera aleatoria. Luego de esta serie, exploraremos otros aspectos de la reproducción multimedia que pueden mejorar la app, como manejar el foco del audio, presentar archivos multimedia de diferentes formas, y reproducir multimedia vía streaming.
1. Crear y Configurar un Proyecto Nuevo
Paso 1
Crea un nuevo proyecto Android. Si estás usando Eclipse, deja que el IDE (Entorno de Desarrollo Integrado) cree una Activity principal y un archivo de layout por tí. Para parte del código que usaremos en esta serie, necesitarás como mínimo API 16, por lo cual tendrás que efectuar pasos adicionales para soportar versiones más antiguas. Una vez creado tu proyecto, abre el archivo Manifest. Dentro del elemento manifest, agrega el siguiente permiso:
1 |
|
2 |
<uses-permission android:name="android.permission.WAKE_LOCK" /> |
Utilizaremos este permiso para lograr que la música continúes reproduciéndose cuando el dispositivo esté inactivo. Tu Manifest ya debería contener un elemento para tu Activity principal. Añade los siguientes atributos al elemento activity para configurar screenOrientation y launchMode:
1 |
|
2 |
<activity
|
3 |
android:name="com.example.musicplayer.MainActivity" |
4 |
android:label="@string/app_name" |
5 |
android:launchMode="singleTop" |
6 |
android:screenOrientation="portrait" > |
Nos apegaremos a la orientación vertical para ganar en simplicidad. El launchMode auxiliará el proceso de navegación de vuelta a la app luego de haberla pasado a segundo plano. Mostraremos una notificación indicando la canción que está siendo reproducida; al tocar la notificación, el usuario será redirigido a la app. Además, utilizaremos un Service para la reproducción de música. Agrega la siguiente línea al Manifest del proyecto, dentro del elemento application, y luego del elemento activity:
1 |
|
2 |
<service android:name="com.example.musicplayer.MusicService" /> |
Modifica el nombre del paquete para que se ajuste al tuyo, y cambia el nombre de la clase si así lo deseas.
Paso 2
Abre el archivo principal de layout de tu proyecto y reemplaza su contenido por el siguiente layout:
1 |
|
2 |
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android" |
3 |
xmlns:tools="http://schemas.android.com/tools" |
4 |
android:layout_width="fill_parent" |
5 |
android:layout_height="fill_parent" |
6 |
android:orientation="vertical" |
7 |
android:background="#FF330000" |
8 |
tools:context=".MainActivity" > |
9 |
|
10 |
<ListView
|
11 |
android:id="@+id/song_list" |
12 |
android:layout_width="fill_parent" |
13 |
android:layout_height="wrap_content" > |
14 |
</ListView>
|
15 |
|
16 |
</LinearLayout>
|
Asegúrate de alterar el atributo tools:context si tu Activity principal tiene otro nombre. El layout incluye una ListView en la cual presentaremos el listado de canciones.
Ahora agregaremos dos ítems al menú para activar y desactivar la función de reproducción aleatoria, y para salir de la aplicación. Abre tu archivo principal de menú (res/menu/main.xml) y reemplaza su contenido con lo siguiente:
1 |
|
2 |
<menu xmlns:android="http://schemas.android.com/apk/res/android" > |
3 |
|
4 |
<item
|
5 |
android:id="@+id/action_shuffle" |
6 |
android:icon="@drawable/rand" |
7 |
android:orderInCategory="1" |
8 |
android:showAsAction="always" |
9 |
android:title="Shuffle"/> |
10 |
|
11 |
<item
|
12 |
android:id="@+id/action_end" |
13 |
android:icon="@drawable/end" |
14 |
android:orderInCategory="2" |
15 |
android:showAsAction="always" |
16 |
android:title="End"/> |
17 |
|
18 |
</menu>
|
Si prefieres, puedes almacenar los strings de los títulos en el archivo res/values/strings.xml. Estos dos ítems se refieren a archivos drawable. Crea uno propio, o emplea estas dos imágenes para comenzar:


También utilizaremos un ícono para mostrar en la notificación de reproducción. Crea uno ahora, o utiliza el que se muestra aquí debajo:

El código se referirá a estas imágenes mediante los nombres rand, end, y play, por lo que debes asegurarte de usar los mismos nombres de archivo. Copia las imágenes en la(s) carpeta(s) de archivos drawable de tu proyecto. Implementaremos estas acciones más tarde.
2. Busca Canciones en el Dispositivo
Paso 1
Ahora, vamos a buscar archivos de audio en el dispositivo del usuario. Primero, agrega una nueva clase a tu proyecto, y llámala Song. Usaremos esta clase para modelar la data de un único archivo de audio. Dentro de la declaración de la clase, agrega tres variables de instancia para los datos que deseamos guardar de cada pista:
1 |
|
2 |
private long id; |
3 |
private String title; |
4 |
private String artist; |
Luego, añade un método constructor en el cual instanciaremos las variables de instancia:
1 |
|
2 |
public Song(long songID, String songTitle, String songArtist) { |
3 |
id=songID; |
4 |
title=songTitle; |
5 |
artist=songArtist; |
6 |
}
|
Finalmente, agrega métodos get para estas variables de instancia:
1 |
|
2 |
public long getID(){return id;} |
3 |
public String getTitle(){return title;} |
4 |
public String getArtist(){return artist;} |
Si planeas utilizar más información de las pistas, siéntete libre de agregar variables de instancia adicionales a la clase.
Paso 2
Abre la Activity principal y agrega las siguientes sentencias de importación:
1 |
|
2 |
import java.util.ArrayList; |
3 |
import java.util.Collections; |
4 |
import java.util.Comparator; |
5 |
import android.net.Uri; |
6 |
import android.content.ContentResolver; |
7 |
import android.database.Cursor; |
8 |
import android.widget.ListView; |
Declara las siguientes variables de instancia antes del método onCreate:
1 |
|
2 |
private ArrayList<Song> songList; |
3 |
private ListView songView; |
Almacenaremos las canciones en una lista y las mostraremos en la instancia de ListView en el layout principal. En onCreate, luego de declarar la vista contenedora, debes obtener la instancia de ListView usando el ID que le dimos en el layout principal.
1 |
|
2 |
songView = (ListView)findViewById(R.id.song_list); |
Instancia la lista como se muestra a continuación:
1 |
|
2 |
songList = new ArrayList<Song>(); |
Ahora, en la declaración de la Activity principal, luego de los métodos que ya existen, crea un método auxiliar para obtener la información del archivo de audio:
1 |
|
2 |
public void getSongList() { |
3 |
//retrieve song info
|
4 |
}
|
Dentro de este método, crea una instancia de ContentResolver, obtén la URI para los archivos de audio externos, y crea una instancia de Cursor usando la instancia de ContentResolver para buscar los archivos de música:
1 |
|
2 |
ContentResolver musicResolver = getContentResolver(); |
3 |
Uri musicUri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; |
4 |
Cursor musicCursor = musicResolver.query(musicUri, null, null, null, null); |
Ahora podemos iterar los resultados, primero chequeando que tenemos datos válidos:
1 |
|
2 |
if(musicCursor!=null && musicCursor.moveToFirst()){ |
3 |
//get columns
|
4 |
int titleColumn = musicCursor.getColumnIndex |
5 |
(android.provider.MediaStore.Audio.Media.TITLE); |
6 |
int idColumn = musicCursor.getColumnIndex |
7 |
(android.provider.MediaStore.Audio.Media._ID); |
8 |
int artistColumn = musicCursor.getColumnIndex |
9 |
(android.provider.MediaStore.Audio.Media.ARTIST); |
10 |
//add songs to list
|
11 |
do { |
12 |
long thisId = musicCursor.getLong(idColumn); |
13 |
String thisTitle = musicCursor.getString(titleColumn); |
14 |
String thisArtist = musicCursor.getString(artistColumn); |
15 |
songList.add(new Song(thisId, thisTitle, thisArtist)); |
16 |
}
|
17 |
while (musicCursor.moveToNext()); |
18 |
}
|
Primero traemos los índices de columna para los ítems de datos que nos interesan de cada canción, luego los usamos para crear un nuevo objeto Song y sumarlo a la lista, antes de continuar iterando los resultados.
De regreso al onCreate, luego del código que agregamos, invoca este nuevo método:
1 |
|
2 |
getSongList(); |
3. Muestra las Canciones
Paso 1
Ya podemos mostrar la lista de canciones en la interfaz de usuario. En el método onCreate, luego de llamar al método auxiliar que creamos recién, ordenaremos los datos para que las canciones se presenten alfabéticamente:
1 |
|
2 |
Collections.sort(songList, new Comparator<Song>(){ |
3 |
public int compare(Song a, Song b){ |
4 |
return a.getTitle().compareTo(b.getTitle()); |
5 |
}
|
6 |
});
|
Utilizamos la variable title de la clase Song, mediante los métodos get que agregamos, para implementar un método compare, que ordene las canciones por título.
Paso 2
Ahora, vamos a definir un layout para representar cada canción de la lista. Crea un nuevo archivo dentro del directorio res/layout de tu proyecto, nómbralo song.xml, y llénalo con lo siguiente:
1 |
|
2 |
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
3 |
xmlns:tools="http://schemas.android.com/tools" |
4 |
android:layout_width="fill_parent" |
5 |
android:layout_height="wrap_content" |
6 |
android:onClick="songPicked" |
7 |
android:orientation="vertical" |
8 |
android:padding="5dp" > |
9 |
|
10 |
<TextView
|
11 |
android:id="@+id/song_title" |
12 |
android:layout_width="fill_parent" |
13 |
android:layout_height="wrap_content" |
14 |
android:textColor="#FFFFFF99" |
15 |
android:textSize="20sp" |
16 |
android:textStyle="bold" /> |
17 |
|
18 |
<TextView
|
19 |
android:id="@+id/song_artist" |
20 |
android:layout_width="fill_parent" |
21 |
android:layout_height="wrap_content" |
22 |
android:textColor="#FFFFFF99" |
23 |
android:textSize="18sp" /> |
24 |
|
25 |
</LinearLayout>
|
Siéntete libre de modificar el layout para que se ajuste a tus preferencias. Cada canción de la lista estará representada por su título y artista, por ende usaremos TextViews para mostrar estos datos. Notarás que la etiqueta de apertura del LinearLayout contiene un atributo onClick. Emplearemos este método en la Activity principal para responder a la selección de canciones del usuario en la lista, reproduciendo la canción representada por el ítem de la lista que fue presionado.
Paso 3
Usaremos un Adapter para relacionar las canciones a la lista. Agrega una nueva clase a tu aplicación, llámala SongAdapter o por un nombre a elección. Al crear la clase, dale la superclase android.widget.BaseAdapter. Eclipse debería insertar la siguiente estructura:
1 |
|
2 |
public class SongAdapter extends BaseAdapter { |
3 |
|
4 |
@Override
|
5 |
public int getCount() { |
6 |
// TODO Auto-generated method stub
|
7 |
return 0; |
8 |
}
|
9 |
|
10 |
@Override
|
11 |
public Object getItem(int arg0) { |
12 |
// TODO Auto-generated method stub
|
13 |
return null; |
14 |
}
|
15 |
|
16 |
@Override
|
17 |
public long getItemId(int arg0) { |
18 |
// TODO Auto-generated method stub
|
19 |
return 0; |
20 |
}
|
21 |
|
22 |
@Override
|
23 |
public View getView(int arg0, View arg1, ViewGroup arg2) { |
24 |
// TODO Auto-generated method stub
|
25 |
return null; |
26 |
}
|
27 |
|
28 |
}
|
Tendrás que agregar las siguientes sentencias de importación:
1 |
|
2 |
import java.util.ArrayList; |
3 |
import android.content.Context; |
4 |
import android.view.LayoutInflater; |
5 |
import android.widget.LinearLayout; |
6 |
import android.widget.TextView; |
Dentro de la declaración de la clase, declara las siguientes variables de instancia:
1 |
|
2 |
private ArrayList<Song> songs; |
3 |
private LayoutInflater songInf; |
Pasaremos la lista de canciones desde la Activity principal, y emplearemos el LayoutInflater para relacionar los valores de título y artista a los TextViews del layout que hemos creado para la canción.
Luego de las variables de instancia, dale al adapter un método constructor para instanciarlas:
1 |
|
2 |
public SongAdapter(Context c, ArrayList<Song> theSongs){ |
3 |
songs=theSongs; |
4 |
songInf=LayoutInflater.from(c); |
5 |
}
|
Modifica el contenido del método getCount para que retorne el tamaño de la lista:
1 |
|
2 |
@Override
|
3 |
public int getCount() { |
4 |
return songs.size(); |
5 |
}
|
Puedes dejar los métodos getItem y getItemId sin tocar. Actualiza la implementación del método getView como se muestra a continuación:
1 |
|
2 |
@Override
|
3 |
public View getView(int position, View convertView, ViewGroup parent) { |
4 |
//map to song layout
|
5 |
LinearLayout songLay = (LinearLayout)songInf.inflate |
6 |
(R.layout.song, parent, false); |
7 |
//get title and artist views
|
8 |
TextView songView = (TextView)songLay.findViewById(R.id.song_title); |
9 |
TextView artistView = (TextView)songLay.findViewById(R.id.song_artist); |
10 |
//get song using position
|
11 |
Song currSong = songs.get(position); |
12 |
//get title and artist strings
|
13 |
songView.setText(currSong.getTitle()); |
14 |
artistView.setText(currSong.getArtist()); |
15 |
//set position as tag
|
16 |
songLay.setTag(position); |
17 |
return songLay; |
18 |
}
|
Declaramos los textos de título y artista, tomando de la lista la instancia correcta de Song usando el índice de posición, uniendo estos strings con las vistas que le añadimos al archivo de layout de la canción. También establecemos la posición como la etiqueta de la vista, lo cual nos permitirá reproducir la canción correcta cuando el usuario seleccione un ítem de la lista. Recuerda que el archivo de layout song.xml incluye un atributo onClick. Usaremos el método allí listado para obtener la etiqueta en la Activity.
Paso 3
De vuelta en la Activity principal, en el método onCreate y luego de ordenar la lista, crea una nueva instancia de la clase Adapter y configúralo en la ListView.
1 |
|
2 |
SongAdapter songAdt = new SongAdapter(this, songList); |
3 |
songView.setAdapter(songAdt); |
Cuando corras la app, debería presentar el listado de canciones que hay en el dispositivo; en este momento, la selección de alguna de ellas provocará que la app tire una excepción, pero implementaremos el manejo del click en el próximo tutorial.
Conclusión
Ya hemos configurado la aplicación para leer canciones del dispositivo del usuario. En la próxima parte, comenzaremos la reproducción cuando el usuario seleccione una canción, utilizando la clase MediaPlayer. Implementaremos la reproducción mediante la clase Service, para que continúe mientras el usuario interactúa con otras apps. Finalmente, utilizaremos la clase MediaController para darle al usuario el control de la reproducción.



