Students Save 30%! Learn & create with unlimited courses & creative assets Students Save 30%! Save Now
Advertisement
  1. Code
  2. Android SDK
Code

Audio de Fondo en Android Con MediaSessionCompat

by
Difficulty:BeginnerLength:LongLanguages:

Spanish (Español) translation by Rafael Chavarría (you can also view the original English article)

Uno de los usos más populares para dispositivos móviles es reproducción de audio mediantes servicios de música en streaming, podcasts descargadoso cualquier otro número de fuentes de audio. Mientras que es una característica bastante común, es difícil de implementar, con muchas piezas diferentes que necesitan integrarse correctamente para poder dar a tu usuario una experiencia Android completa.

En este tutorial aprenderás acerca de MediaSessionCompat desde la librería de soporte de Android, y cómo puede ser usada para crear un servicio apropiado de audio de fondo para tus usuarios.

Configuración

La primera cosa que necesitarás es incluír la librería de soporte de Android en tu proyecto. Esto se puede hacer agregando la siguiente línea en el archivo build.gradle de tu módulo bajo el nodo de dependencias.

Después de que has sincronizado tu proyecto, crea una nueva clase Java. Para este ejemplo llamaré a la clase BackgroundAudioService. Esta clase necesitará extender a MediaBrowserServiceCompat. También implementaremos las siguientes interfaces: MediaPlayer.OnCompletionListener y AudioManager.OnAudioFocusChangeListener.

Ahora que tu implementación de MediaBrowserServiceCompat está creada, tomemos un momento para actualizar el AndroidManifest.xml antes de regersar a esta clase. En la parte superior de la clase, necesitarás crear la petición del permiso WAKE_LOCK.

Después, dentro del nodo application, declara tu nuevo servicio con los siguientes objetos intent-filter. Estos permitirán a tu servicio interceptar botones de control, eventos de audífonos y exploración de medios para dispositivos, tales como Android Auto (aunque no haremos nada con Android Auto para este tutorial, algún soporte básico para este es aún requerido por MediaBrowserServiceCompat).

Finalmente, necesitarás declarar el uso de MediaButtonReceiver desde la librería de soporte de Android. Esto te permitirá interceptar interacciones de botones de control de medios y eventos de audífonos en dispositivos corriendo KitKat y anteriores.

Ahora que tu archivo AndroidManifest.xml está terminado, puedes cerrarlo. También vamos a crear otra clase llamada MediaStyleHelper, la cual fue escrita por Ian Lake, Desarrollador Defensor en Google, para limpiar la creación de estilo de notificaciones de medios.

Una vez creada, procede a cerrar el archivo. Nos concentraremos en el servicio de audio de fondo en la siguiente sección.

Construyendo el Servicio de Audio de Fondo

Ahora es tiempo de adentrarse en el núcleo de la creación de tu app de medios. Hay algunas cuantas variables que querrás declarar primero para esta aplicación de muestra: un MediaPlayer la reproducción y un objeto MediaSessionCompat que administrará metadatos y controles/estados de reproducción.

Adicionalmente, necesitarás un BroadcastReceiver que escuche cambios en el estado de los audífonos. Para mantener las cosas simples, este receptor pausará el MediaPlayer, si está reproduciéndose.

Para la variable final, necesitarás crear un objeto MediaSessionCompat.Callback, el cuál es usado para manejar el estado de reproducción cuando ocurren actiones de sesión de medios.

Revisitaremos cada uno de los métodos anteriores más adelante en este tutorial, ya que serán usados para manejar operaciones en nuesta app de medios.

Hay dos métodos que también necesitaremos declarar, aunque no necesitarán hacer nada para los propósitos de este tutorial: onGetRoot() y onLoadChildren(). Puedes usar el siguiente código para tus valores por defecto.

Por último, querrás anular el método onStartCommand(), el cuál es el punto de entrada a tu Service. Este método tomará el Intent que se pasa al Service y lo manda a la clase MediaButtonReceiver.

Inicializando Todas las Cosas

Ahora que tus variables base están creadas, es tiempo de inicializar todo. Haremos esto llamando varios métodos de ayuda en onCreate().

El primer método, initMediaPlayer(), inicializará el objeto MediaPlayer que creamos al inicio de la clase, solicitar un bloqueo de inicio parcial (por lo que requerimos ese permiso en AndroidManifest.xml), y fijamos el volumen del reproductor.

El siguiente método, initMediaSession(), es donde inicializamos el objeto MediaSessionCompat y lo enlazamos a los métodos de botones y control que nos permiten manejar la reproducción y entrada del usuario. Este método inicia creando un objeto ComponentName que apunta a la clase MediaButtonReceiver de la librería de soporte de Android y la usa para crear un nuevo MediaSessionCompat. Entonces pasamos el objeto MediaSession.Callback que creamos anteriormente para este y fijamos las banderas necesarias para recibir señales de botones de entrada y control. Después, creamos un nuevo Intent para manejar botones de entrada de medios en dispositivos pre-Lollipop y fijamos el media session token para nuestro servicio.

Finalmente, registraremos el BroadcastReceiver que creamos al inicio de la clase para que podamos escuchar eventos de cambios en audífonos.

Manejando Audio Focus

Ahora que has termindado de inicializar los objetos BroadcastReceiver, MediaSessionCompat y MediaPlayer, es tiempo de echar un vistazo al manejo de audio focus.

Mientras que podemos pensar que nuestras propias apps de audio son las más importantes por el momento, otras apps en el dispositivo estarán compitiendo por hacer sus propios sonidos, tales como la notificación de email o un juego móvil. Para poder trabajar con estas distintas situaciones, el sitema Android usa audio focus para determinar como debería ser manejado el audio.

EL primer caso que querremos manejar es comenzar a reproducir e intentar recibir la atención del dispositivo. En tu objeto MediaSessionCompat.Callback, ve al método onPlay() y agrega la siguiente verificación de condición.

El código anterior llamará al método de ayuda que intenta atraer atención y si no puede, simplemente regresa. En la app real, quisieras manejar reproducción fallida de audio de mejor manera. successfullyRetrievedAudioFocus() obtendrá una referencia al AudioManager del sistema e intentará solicitar atención de audio para reproducir música. Entonces regresará un boolean representando si la petición fue exitosa o no.

Notarás que también estamos pasando this al método requestAudioFocus(), lo que asocia al OnAudioFocusChangeListener con nuestro servicio. Hay algunos cuantos estados diferentes que querrás escuchar para ser un "buen ciudadano" en el ecosistema de la app del dispositivo.

  • AudioManager.AUDIOFOCUS_LOSS: Esto ocurre cuando otra app ha solicitado atención de audio. Cuando esto sucede, deberías detener la reproducción de audio en tu app.
  • AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: Este estado es ingresado cuando otra aplicación quiere reproducir audio, pero solo anticipa necesitar foco por un periodo corto de tiempo. Puedes usar este estado para pauser tu reproducción de audio.
  • AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:Cuando la atención de audio es solicitada, pero arroja un estado 'can duck', significa que puedes continuar tu reproducciónm pero deberías bajar el volumen un poco. Esto puede ocurrir cunado un sonido de notificación es reproducida por el dispositivo.
  • AudioManager.AUDIOFOCUS_GAIN: El último estado que discutiremos es AUDIOFOCUS_GAIN. Este es el estado cuando la reproducción acoplable de audio se ha completado y tu app puede reanudar sus niveles previos.

Un callback onAudioFocusChange() pudiera verse como esto:

Entendiendo el MediaSessionCompat.Callback

Ahora que tienes una estructura general para tu Service, es tiempo de entrar en el MediaSessionCompat.Callback. En la última sección agregaste un poco a onPlay() para revisar si la atención de audio fue otorgada. Debajo de la declaración condicional, querrás fijar el objeto MediaSessionCompat a activo, darle un estado de STATE_PLAYING y asignar las acciones apropiadas necesarias para crear botones de pausa en controles de bloqueo de pantalla pre-Lollipop, notificaciones de teléfono y Android Wear.

El método setMediaPlaybackState() anterior es un método de ayuda que crea un objeto PlaybackStateCompat.Builder y provee las acciones y estado apropiados y después construye y asocia un PlaybackStateCompat con tu objeto MediaSessionCompat.

Es importante que notes que necesitarás tanto la bandera ACTION_PLAY_PAUSE y la ACTION_PAUSE or ACTION_PLAY en tus acciones para tener controles apropiados en Android Wear.

Media notification on Android Wear

De regreso en onPlay(), querrás mostrar una notificación de reproducción que está asociada con tu objeto MediaSessionCompat al usar la clase MediaStyHelper que definimos anteriormente y después mostrar esa notificación.

Finalmente, empezarás el MediaPlayer al final de onPlay().

Media control notification on an Android Nougat device

Cuando el callback recibe un comando de pausa, onPause() será llamado. Aquí pausarás el MediaPlayer, fijarás el estado a STATE_PAUSED y mostrarás una notificación pausada.

Nuestro método ayudante showPausedNotification() trabajará de manera similar al método showPlayNotification().

El siguiente método en el callback que discutiremos, onPlayFromMediaId(), toma un String y un Bundle como parámetros. Este es el método callback que puedes usar para cambiar pistas de audio/contenido dentro de tu aplicación.

Para este tutorial, simplemente aceptaremos un ID de recurso en bruto e intentaremos reproducir eso y después reinicializar los metadatos de la sesión. Mientras que es permitido pasar el Bundle a este método, puedes usarlo para personalizar otros aspectos de tu reproducción de medios, como fijar una un sonido de fondo personalizado de fondo para una pista.

Ahora que hemos discutido los dos métodos principales en este callback que usarás en tus apps, es importante saber que hay otros métodos opcionales que puedes usar para personalizar tu servicio. Algunos métodos incluyen onSeekTo(), el cuál te permite cambiar la posición de reproducción de tu contenido y onCommand(), el cuál acepta un String denotando el tipo de comando, un Bundle para información extra acerca del comando y un callback ResultReceiver, el cuál te permitirá enviar comandos pesonalizados a tu Service.

Desbaratando

Cuando nuestro archivo de audio se ha completado, querremos decidir cuál será la siguiente acción. Mientras que tal vez quieras reproducir la siguiente pista en tu app, mantendremos las cosas simples y liberaremos el MediaPlayer.

Finalmente, vamos a querer hacer unas cuantas cosas en el método onDestroy() de nuestro Service. Primero, obtenemos una referencia del servicio de sistema AudioManager y llamamos abandonAudioFocus() con nuestro AudioFocusChangeListener como parámetro, el cuál notificará a otras aplicaciones en el dispositivo que estarás cediendo la atención del audio. Después, anular el registro de BroadcastReceiver que se configuró para escuchar cambios de audífonos y liberar el objeto MediaSessionCompat. Finalmente, deberás querer cancelar la notificación de control de reproducción.

En este punto, deberías tener un Service básico de audio de fondo usando MediaSessionCompat para control de reproducción a través de dispositivos. Mientras que mucho se ha visto involucrado en crear el servicio, deberías poder controlar la reproducción desde tu app, una notificación, controles en dispositivos pre-Lollipop (Lollipop y posteriores usarán una notifiación en la pantalla de bloqueo), y desde dispositivos periféricos, como Android Wear, una vez que el Service ha sido iniciado.

Media lock screen controls on Android Kit Kat

Iniciando y Controlando Contenido desde una Activity

Mientras que la mayoría de los controles serán automáticos, aún tendrás un poco de trabajo para iniciar y controlar una sesión de medios desde los controles de tu app. Mínimo, vas a querer objetos MediaBrowserCompat.ConnectionCallback, MediaControllerCompat.Callback, MediaBrowserCompat y MediaControllerCompat creados en tu app.

MediaControllerCompat.Callback tendrá un método llamado onPlaybackStateChanged() que recibe cambios en el estado de reproducción y que puede ser usado para mantener tu UI en sincronía.

MediaBrowserCompat.ConnectionCallback tiene un método onConnected() que será llamado cuando un nuevo objeto MediaBrowserCompat es creado y conectado. Puedes usar esto para inicializar tu objeto MediaControllerCompat, enlazarlo a tu MediaControllerCompat.Callback y asociarlo con MediaSessionCompat desde tu Service. Una vez que está completo, puedes iniciar tu reproducción de audio desde este método.

Notarás que el siguiente código usa getSupportMediaController().getTransportControls() para comunicarse con la sesión de medios. Usando la misma técnica, puedes llamar tu onPlay() y onPause() en el objeto MediaSessionCompat.Callback de tu servicio de audio.

Cuando termines con tu reproducción de audio, puedes pausar el servicio de audio y desconectar tu objeto MediaBrowserCompat, el cual haremos en este tutorial cuando esta Activity se destruya.

Concluyendo

Whew! Como puedes ver, hay muchas piezas móviles involucradas en crear y usar un servicio de audio de fondo de manera correcta.

En este tutorial, has creado un Service que reproduce un simple archivo de audio, escucha por cambios en la atención del audio y enlaza a MediaSessionCompat para proveer control universal de reproducción en dispositivos Android, incluyendo auriculares y Android Wear. Si te encuentras con bloqueos mientras trabajas con este tutorial, te recomiendo ampliamente revisar el código asociado al proyecto Android en el GitHub de Envato Tuts+.

Y revisa algunos de nuestros otros cursos y tutoriales de Android aquí en Envato Tuts+!.


Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.