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.
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()
}
}
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()
.
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
}
}
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:
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)
}
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:
private var controllerCallback = object : MediaControllerCompat.Callback() {
override fun onMetadataChanged(metadata: MediaMetadataCompat?) {}
override fun onPlaybackStateChanged(state: PlaybackStateCompat?) {}
}
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:
private var controllerCallback = object : MediaControllerCompat.Callback() {
override fun onSessionDestroyed() {
mediaBrowser.disconnect()
// maybe schedule a reconnection using a new MediaBrowser instance
}
}
MediaControllerCompat.Callback controllerCallback =
new MediaControllerCompat.Callback() {
@Override
public void onSessionDestroyed() {
mediaBrowser.disconnect();
// maybe schedule a reconnection using a new MediaBrowser instance
}
};