Para completar el diseño de cliente-servidor, debes compilar un componente de actividad que contenga tu código de IU, un MediaController asociado y un MediaBrowser.
El MediaBrowser realiza dos funciones importantes: se conecta a un MediaBrowserService y, al conectarse, crea el MediaController para la IU.
Nota: La implementación recomendada de MediaBrowser
es MediaBrowserCompat
,
que se define en el
Biblioteca de compatibilidad Media-Compat
En esta página, el término "MediaBrowser" se refiere a una instancia
de MediaBrowserCompat.
Conéctate al MediaBrowserService
Cuando se crea tu actividad de cliente, se conecta al MediaBrowserService. Esto implica un protocolo de enlace. Modifica las devoluciones de llamada del ciclo de vida de la actividad de la siguiente manera:
onCreate()
construye un MediaBrowserCompat. Pasa el nombre de tu MediaBrowserService y el MediaBrowserCompat.ConnectionCallback que definiste.onStart()
se conecta al MediaBrowserService. Aquí es donde entra en juego la magia de MediaBrowserCompat.ConnectionCallback. Si la conexión es exitosa, la devolución de llamada onConnect() crea el controlador multimedia, lo vincula a la sesión multimedia, vincula los controles de tu IU al MediaController y registra el controlador para recibir devoluciones de llamada de la sesión multimedia.onResume()
configura la transmisión de audio para que tu app responda al control de volumen del dispositivo.onStop()
desconecta el MediaBrowser y anula el registro de MediaController.Callback cuando se detiene tu actividad.
Kotlin
class MediaPlayerActivity : AppCompatActivity() { private lateinit var mediaBrowser: MediaBrowserCompat override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... // Create MediaBrowserServiceCompat mediaBrowser = MediaBrowserCompat( this, ComponentName(this, MediaPlaybackService::class.java), connectionCallbacks, null // optional Bundle ) } public override fun onStart() { super.onStart() mediaBrowser.connect() } public override fun onResume() { super.onResume() volumeControlStream = AudioManager.STREAM_MUSIC } public override fun onStop() { super.onStop() // (see "stay in sync with the MediaSession") MediaControllerCompat.getMediaController(this)?.unregisterCallback(controllerCallback) mediaBrowser.disconnect() } }
Java
public class MediaPlayerActivity extends AppCompatActivity { private MediaBrowserCompat mediaBrowser; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // ... // Create MediaBrowserServiceCompat mediaBrowser = new MediaBrowserCompat(this, new ComponentName(this, MediaPlaybackService.class), connectionCallbacks, null); // optional Bundle } @Override public void onStart() { super.onStart(); mediaBrowser.connect(); } @Override public void onResume() { super.onResume(); setVolumeControlStream(AudioManager.STREAM_MUSIC); } @Override public void onStop() { super.onStop(); // (see "stay in sync with the MediaSession") if (MediaControllerCompat.getMediaController(MediaPlayerActivity.this) != null) { MediaControllerCompat.getMediaController(MediaPlayerActivity.this).unregisterCallback(controllerCallback); } mediaBrowser.disconnect(); } }
Personaliza MediaBrowserCompat.ConnectionCallback
Cuando tu actividad construye MediaBrowserCompat, debes crear una instancia de ConnectionCallback. Modifica su método onConnected()
a fin de recuperar el token de la sesión multimedia del MediaBrowserService y usa el token para crear un MediaControllerCompat.
Usa el método de conveniencia
MediaControllerCompat.setMediaController()
para guardar un vínculo al controlador. Esto permite administrar los botones de medios. También te permite llamar
MediaControllerCompat.getMediaController()
para recuperar el controlador cuando se compilan los controles de transporte.
En el siguiente ejemplo de código, se muestra cómo modificar el método onConnected()
.
Kotlin
private val connectionCallbacks = object : MediaBrowserCompat.ConnectionCallback() { override fun onConnected() { // Get the token for the MediaSession mediaBrowser.sessionToken.also { token -> // Create a MediaControllerCompat val mediaController = MediaControllerCompat( this@MediaPlayerActivity, // Context token ) // Save the controller MediaControllerCompat.setMediaController(this@MediaPlayerActivity, mediaController) } // Finish building the UI buildTransportControls() } override fun onConnectionSuspended() { // The Service has crashed. Disable transport controls until it automatically reconnects } override fun onConnectionFailed() { // The Service has refused our connection } }
Java
private final MediaBrowserCompat.ConnectionCallback connectionCallbacks = new MediaBrowserCompat.ConnectionCallback() { @Override public void onConnected() { // Get the token for the MediaSession MediaSessionCompat.Token token = mediaBrowser.getSessionToken(); // Create a MediaControllerCompat MediaControllerCompat mediaController = new MediaControllerCompat(MediaPlayerActivity.this, // Context token); // Save the controller MediaControllerCompat.setMediaController(MediaPlayerActivity.this, mediaController); // Finish building the UI buildTransportControls(); } @Override public void onConnectionSuspended() { // The Service has crashed. Disable transport controls until it automatically reconnects } @Override public void onConnectionFailed() { // The Service has refused our connection } };
Conecta tu IU al controlador multimedia
En el código de muestra de ConnectionCallback anterior, incluye una llamada a buildTransportControls()
para completar tu IU. Deberás configurar onClickListeners para los elementos de la IU que controlan el reproductor. Elige las opciones
MediaControllerCompat.TransportControls
para cada uno.
Tu código tendrá un aspecto similar al siguiente, con un onClickListener para cada botón:
Kotlin
fun buildTransportControls() { val mediaController = MediaControllerCompat.getMediaController(this@MediaPlayerActivity) // Grab the view for the play/pause button playPause = findViewById<ImageView>(R.id.play_pause).apply { setOnClickListener { // Since this is a play/pause button, you'll need to test the current state // and choose the action accordingly val pbState = mediaController.playbackState.state if (pbState == PlaybackStateCompat.STATE_PLAYING) { mediaController.transportControls.pause() } else { mediaController.transportControls.play() } } } // Display the initial state val metadata = mediaController.metadata val pbState = mediaController.playbackState // Register a Callback to stay in sync mediaController.registerCallback(controllerCallback) }
Java
void buildTransportControls() { // Grab the view for the play/pause button playPause = (ImageView) findViewById(R.id.play_pause); // Attach a listener to the button playPause.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // Since this is a play/pause button, you'll need to test the current state // and choose the action accordingly int pbState = MediaControllerCompat.getMediaController(MediaPlayerActivity.this).getPlaybackState().getState(); if (pbState == PlaybackStateCompat.STATE_PLAYING) { MediaControllerCompat.getMediaController(MediaPlayerActivity.this).getTransportControls().pause(); } else { MediaControllerCompat.getMediaController(MediaPlayerActivity.this).getTransportControls().play(); } }); MediaControllerCompat mediaController = MediaControllerCompat.getMediaController(MediaPlayerActivity.this); // Display the initial state MediaMetadataCompat metadata = mediaController.getMetadata(); PlaybackStateCompat pbState = mediaController.getPlaybackState(); // Register a Callback to stay in sync mediaController.registerCallback(controllerCallback); } }
Los métodos TransportControls envían devoluciones de llamada a la sesión multimedia de tu servicio. Asegúrate de haber definido un modelo de
MediaSessionCompat.Callback
para cada control.
Mantén la sincronización con la sesión multimedia
La IU debe mostrar el estado actual de la sesión multimedia, tal como lo describen su PlaybackState y Metadata. Cuando creas los controles de transporte, puedes tomar el estado actual de la sesión, mostrarlo en la IU y habilitar o inhabilitar los controles de transporte en función del estado y sus acciones disponibles.
Para recibir devoluciones de llamada de la sesión multimedia cada vez que cambien su estado o sus metadatos, define un
MediaControllerCompat.Callback
, con estos dos métodos:
Kotlin
private var controllerCallback = object : MediaControllerCompat.Callback() { override fun onMetadataChanged(metadata: MediaMetadataCompat?) {} override fun onPlaybackStateChanged(state: PlaybackStateCompat?) {} }
Java
MediaControllerCompat.Callback controllerCallback = new MediaControllerCompat.Callback() { @Override public void onMetadataChanged(MediaMetadataCompat metadata) {} @Override public void onPlaybackStateChanged(PlaybackStateCompat state) {} };
Registra la devolución de llamada cuando compiles los controles de transporte (consulta el método buildTransportControls()
) y anula el registro cuando se detenga la actividad (en el método del ciclo de vida onStop()
de la actividad).
Desconectar cuando se destruye la sesión multimedia
Si la sesión multimedia deja de ser válida,
onSessionDestroyed()
se emite la devolución de llamada. Cuando eso sucede, la sesión no puede volver a funcionar
de nuevo durante la vida útil de MediaBrowserService
. Si bien las funciones
relacionados con MediaBrowser
podrían seguir funcionando, el usuario no podrá ver ni controlar
la reproducción desde una sesión multimedia destruida, lo que probablemente disminuirá el valor de
tu aplicación.
Por lo tanto, cuando se destruye la sesión, debes desconectarte de la
Llamando a MediaBrowserService
disconnect()
Esto garantiza que el servicio de navegador no tenga clientes vinculados y
pueden ser destruidas por
(SO).
Si necesitas volver a conectarte a MediaBrowserService
más tarde (por ejemplo, si
tu aplicación quiere mantener una conexión persistente con la app de música),
Crea una instancia nueva de MediaBrowser
en lugar de volver a usar la anterior.
El siguiente fragmento de código demuestra una implementación de devolución de llamada que Se desconecta del servicio de navegador cuando se destruye la sesión multimedia:
Kotlin
private var controllerCallback = object : MediaControllerCompat.Callback() { override fun onSessionDestroyed() { mediaBrowser.disconnect() // maybe schedule a reconnection using a new MediaBrowser instance } }
Java
MediaControllerCompat.Callback controllerCallback = new MediaControllerCompat.Callback() { @Override public void onSessionDestroyed() { mediaBrowser.disconnect(); // maybe schedule a reconnection using a new MediaBrowser instance } };