Como criar um cliente de navegador de mídia

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, que é definida na Biblioteca de Suporte media-compat. Ao longo desta página, o termo "MediaBrowser" refere-se a uma instância de 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 que você chame MediaControllerCompat.getMediaController() para recuperar 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 o método apropriado de 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. Verifique se você definiu um método MediaSessionCompat.Callback correspondente 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()).