Cómo usar sesiones multimedia

Las sesiones multimedia proporcionan una manera universal de interactuar con un reproductor de audio o video. Si informas a Android que se está reproduciendo contenido multimedia en una app, se pueden delegar los controles de reproducción a esta. La integración con la sesión multimedia permite que una app anuncie la reproducción de contenido multimedia externamente y reciba comandos de reproducción de fuentes externas. Estas fuentes pueden ser botones físicos (como el botón de reproducción de los auriculares o el control remoto de la TV) o comandos indirectos (como indicar "pausar" a Asistente de Google). Luego, la sesión multimedia delega estos comandos a la app, que los aplica al reproductor multimedia transparente donde se originaron.

Una sesión multimedia existe junto al reproductor que administra. Debes crear e inicializar una sesión multimedia en el método onCreate() de la actividad o el servicio que posee la sesión multimedia y su reproductor asociado.

Cómo inicializar la sesión multimedia

Una sesión multimedia recién creada no tiene capacidades. Debes inicializar la sesión con estos pasos:

  • Establece marcas para que la sesión multimedia pueda recibir devoluciones de llamada desde controladores multimedia y botones de medios.
  • Crea e inicializa una instancia de PlaybackStateCompat y asígnala a la sesión. El estado de reproducción cambia a lo largo de la sesión, por lo que recomendamos almacenar en caché el objeto PlaybackStateCompat.Builder para su reutilización.
  • Crea una instancia de MediaSessionCompat.Callback y asígnala a la sesión (más abajo se brinda información sobre las devoluciones de llamada).

Debes crear e inicializar una sesión multimedia en el método onCreate() de la actividad o el servicio que posee la sesión.

Para que los botones multimedia funcionen cuando tu app se haya inicializado (o detenido) recientemente, su PlaybackState debe contener una acción de reproducción que coincida con el intent que envía el botón multimedia. Es por eso que ACTION_PLAY se asigna al estado de la sesión durante la inicialización. Para obtener más información, consulta Cómo responder a botones de contenido multimedia.

Cómo mantener el estado y los metadatos de la reproducción

Existen dos clases que representan el estado de una sesión multimedia.

La clase PlaybackStateCompat describe el estado operativo actual del reproductor. Esto incluye lo siguiente:

  • El estado del transporte (si el reproductor está reproduciendo contenido, detenido, almacenando contenido en el búfer, etc., consulta getState())
  • Un código de error y un mensaje de error opcional cuando corresponda (consulta getErrorCode() y lee la sección Estados y errores, que se incluye más abajo)
  • La posición del reproductor
  • Las acciones válidas del controlador que se pueden manejar en el estado actual

La clase MediaMetadataCompat describe el material que se está reproduciendo:

  • El nombre del artista, el álbum y la pista
  • La duración de la pista
  • La portada del álbum para mostrar en la pantalla de bloqueo (la imagen es un mapa de bits con un tamaño máximo de 320 x 320 dp; si es más grande, se reduce)
  • Una instancia de ContentUris que dirige a una versión más grande de la portada

El estado y los metadatos del reproductor pueden cambiar durante el ciclo de vida de una sesión multimedia. Cada vez que el estado o los metadatos cambien, debes usar el compilador correspondiente para cada clase, PlaybackStateCompat.Builder() o MediaMetadataCompat.Builder(), y, luego, pasar la nueva instancia a la sesión multimedia llamando a setPlaybackState() o setMetaData(). Para reducir el consumo general de memoria de estas operaciones frecuentes, conviene crear los compiladores una vez y reutilizarlos durante el ciclo de vida de la sesión.

Estados y errores

Ten en cuenta que PlaybackState es un objeto que contiene valores separados para el estado de reproducción de la sesión (getState()) y, cuando es necesario, un código de error asociado (getErrorCode()). Los errores pueden ser irrecuperables o recuperables:

Cada vez que se interrumpa la reproducción, debes generar un error irrecuperable: establece el estado de transporte en STATE_ERROR y especifica un error asociado con setErrorMessage(int, CharSequence). Mientras el error bloquee la reproducción, PlaybackState continuará informando STATE_ERROR y el error.

Se produce un error recuperable cuando tu app no puede controlar una solicitud, pero puede seguir reproduciéndose. El transporte permanece en un estado "normal" (como STATE_PLAYING), pero PlaybackState contiene un código de error. Por ejemplo, si se está reproduciendo la última canción y el usuario solicita pasar a la siguiente canción, la reproducción puede continuar, pero debes crear una PlaybackState nueva con el código de error ERROR_CODE_END_OF_QUEUE y, luego, llamar a setPlaybackState(). Los controladores multimedia adjuntos a la sesión recibirán la devolución de llamada onPlaybackStateChanged() y le explicarán al usuario lo que sucedió. Un error recuperable solo se debe informar una vez, en el momento en que ocurre. La próxima vez que la sesión actualice el PlaybackState, no vuelvas a establecer el mismo error recuperable (a menos que el error se haya producido en respuesta a una nueva solicitud).

Pantallas de bloqueo de la sesión multimedia

A partir de Android 4.0 (nivel de API 14), el sistema puede acceder a los metadatos y al estado de reproducción de una sesión multimedia. Así es como la pantalla de bloqueo puede mostrar los controles multimedia y el material gráfico. El comportamiento varía según la versión de Android.

Portada del álbum

En Android 4.0 (nivel de API 14) hasta Android 10 (nivel de API 29), el fondo de la pantalla de bloqueo muestra la portada del álbum, pero solo si los metadatos de la sesión multimedia incluyen un mapa de bits de fondo.

Controles de transporte

En Android 4.0 (API nivel 14) y hasta Android 4.4 (API nivel 19), cuando una sesión multimedia está activa y los metadatos de la sesión multimedia incluyen un mapa de bits en segundo plano, la pantalla de bloqueo muestra automáticamente los controles de transporte.

En Android 5.0 (nivel de API 21) o versiones posteriores, el sistema no proporciona controles de transporte en la pantalla de bloqueo. En su lugar, debes usar una notificación MediaStyle para mostrar los controles de transporte.

Agrega acciones personalizadas

Las aplicaciones de música pueden definir acciones personalizadas, por ejemplo, Me gusta, Me gusta o retroceder 30 segundos. Una acción personalizada debe implementar un comportamiento completamente nuevo. No uses una acción personalizada para reemplazar una de las acciones de control de transporte estándar definidas en PlaybackStateCompat.

Agrega acciones personalizadas con addCustomAction(). En el siguiente ejemplo, se muestra cómo agregar un control para una acción de Me gusta:

Kotlin

stateBuilder.addCustomAction(
        PlaybackStateCompat.CustomAction.Builder(
                CUSTOM_ACTION_THUMBS_UP,
                resources.getString(R.string.thumbs_up),
                thumbsUpIcon
        ).run {
            setExtras(customActionExtras)
            build()
        }
)

Java

stateBuilder.addCustomAction(new PlaybackStateCompat.CustomAction.Builder(
    CUSTOM_ACTION_THUMBS_UP, resources.getString(R.string.thumbs_up), thumbsUpIcon)
    .setExtras(customActionExtras)
    .build());

Consulta el Universal Music Player para ver un ejemplo completo.

Responde a la acción con onCustomAction().

Kotlin

override fun onCustomAction(action: String, extras: Bundle?) {
    when(action) {
        CUSTOM_ACTION_THUMBS_UP -> {
            ...
        }
    }
}

Java

@Override
public void onCustomAction(@NonNull String action, Bundle extras) {
    if (CUSTOM_ACTION_THUMBS_UP.equals(action)) {
        ...
    }
}

Consulta también el Universal Music Player.

Devoluciones de llamada de sesiones multimedia

Los métodos principales de devolución de llamada son onPlay(), onPause() y onStop(). Aquí es donde agregas el código que controla tu reproductor.

Dado que creas una instancia de la devolución de llamada de la sesión y la estableces en el entorno de ejecución (en onCreate()), tu app puede definir devoluciones de llamada alternativas que usen reproductores distintos y elegir la combinación apropiada de devolución de llamada/reproductor en función del nivel del dispositivo o sistema. Puedes cambiar el reproductor sin modificar el resto de la app. Por ejemplo, puedes usar ExoPlayer si ejecutas Android 4.1 (API nivel 16) o versiones posteriores y usar MediaPlayer en sistemas previos.

Además de controlar el reproductor y administrar las transiciones de estado de la sesión multimedia, las devoluciones de llamada también habilitan e inhabilitan las funciones de tu app y controlan la forma en la que interactúa con otras aplicaciones y con el hardware del dispositivo. (Consulta Cómo controlar la salida de audio).

La implementación de los métodos de devolución de llamada de la sesión multimedia depende de la estructura de tu app. Consulta las páginas separadas en las que se describe cómo usar devoluciones de llamada en apps de audio y apps de video, y cómo se deben implementar las devoluciones de llamada para cada tipo de app.