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 la biblioteca de compatibilidad media-compat.
En esta página, el término "MediaBrowser" hace referencia 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 a MediaControllerCompat.getMediaController()
para recuperar el controlador cuando compilas 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 el método MediaControllerCompat.TransportControls
apropiado 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 método MediaSessionCompat.Callback
correspondiente 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 cambia el estado o los 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).