Para concluir o design de cliente/servidor, você precisa criar um componente de atividade que contenha o código da IU, um MediaController associado e um MediaBrowser.
O MediaBrowser executa duas funções importantes: ele se conecta a um MediaBrowserService e, ao se conectar, cria o MediaController para a IU.
Observação : a implementação recomendada do MediaBrowser
é MediaBrowserCompat
,
definido na
Biblioteca de Suporte Media-Compat.
Nesta página, o termo "MediaBrowser" se refere a uma instância
do MediaBrowserCompat.
Conectar-se ao MediaBrowserService
Quando a atividade de cliente é criada, ela se conecta ao MediaBrowserService. Eles se completam e funcionam em harmonia. Modifique os callbacks do ciclo de vida da atividade da seguinte maneira:
onCreate()
constrói um MediaBrowserCompat. Transmita o nome do MediaBrowserService e do MediaBrowserCompat.ConnectionCallback que você definiu.onStart()
se conecta ao MediaBrowserService. É aqui que entra a mágica do MediaBrowserCompat.ConnectionCallback. Se a conexão se completar, o callback de onConnect() criará o controlador de mídia, vinculará à sessão de mídia, vinculará os controles de IU ao MediaController e registrará o controlador para receber callbacks da sessão de mídia.onResume()
define o stream de áudio para que o app responda ao controle de volume no dispositivo.onStop()
desconecta o MediaBrowser e cancela o registro do MediaController.Callback quando a atividade for interrompida.
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(); } }
Personalizar MediaBrowserCompat.ConnectionCallback
Quando sua atividade construir MediaBrowserCompat, você precisará criar uma instância de ConnectionCallback. Modifique o método onConnected()
para recuperar o token de sessão de mídia do MediaBrowserService e use o token para criar um MediaControllerCompat.
Use o método de conveniência
MediaControllerCompat.setMediaController()
para salvar um link para o controlador. Isso permite o processamento de botões de mídia. Ele também permite chamar
MediaControllerCompat.getMediaController()
para extrair o controlador ao criar os controles de transporte.
O exemplo de código a seguir mostra como modificar o 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 } };
Conectar a IU ao controlador de mídia
O código de exemplo ConnectionCallback acima, inclui uma chamada para buildTransportControls()
para detalhar a IU. Você precisará definir onClickListeners para os elementos de IU que controlam o player. Escolha a opção apropriada
MediaControllerCompat.TransportControls
para cada um.
O código será parecido com este, com um onClickListener para cada botão:
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); } }
Os métodos TransportControls enviam callbacks para a sessão de mídia do serviço. Certifique-se de que você tenha definido uma
MediaSessionCompat.Callback
para cada controle.
Fique sincronizado com a sessão de mídia
A IU precisa exibir o estado atual da sessão de mídia, conforme descrito pelo PlaybackState e pelos metadados. Ao criar os controles de transporte, você pode capturar o estado atual da sessão, exibi-lo na IU e ativar e desativar os controles de transporte com base no estado e nas ações disponíveis.
Para receber callbacks da sessão de mídia sempre que o estado ou os metadados mudarem, defina um
MediaControllerCompat.Callback
com estes dois 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) {} };
Registre o callback ao criar os controles de transporte (consulte o método buildTransportControls()
) e cancele o registro quando a atividade for interrompida (no método de ciclo de vida do onStop()
).
Desconectar quando a sessão de mídia for destruída
Se a sessão de mídia se tornar inválida, o
onSessionDestroyed()
é emitido um callback. Quando isso acontece, a sessão não pode se tornar funcional
novamente durante o ciclo de vida da MediaBrowserService
. Embora as funções
relacionadas a MediaBrowser
podem continuar funcionando, um usuário não pode ver nem controlar
a reprodução de uma sessão de mídia destruída, o que provavelmente diminuirá o valor do
seu aplicativo.
Portanto, quando a sessão for destruída, você deve se desconectar do
MediaBrowserService
ao chamar
disconnect()
Isso garante que o serviço do navegador não tenha clientes vinculados e
podem ser destruídos pela
SO.
Se você precisar se reconectar ao MediaBrowserService
mais tarde (por exemplo, se
seu aplicativo quiser manter uma conexão permanente com o app de música),
crie uma nova instância de MediaBrowser
em vez de reutilizar a antiga.
O snippet de código a seguir demonstra uma implementação de callback que desconecta do serviço do navegador quando a sessão de mídia é destruída:
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 } };