构建媒体浏览器客户端

要完成客户端/服务器设计,您必须构建包含界面代码、关联的 MediaController 和 MediaBrowser 的 Activity 组件。

MediaBrowser 执行两项重要功能:连接到 MediaBrowserService,并在连接后为您的界面创建 MediaController。

注意:MediaBrowser 的建议实现方式是 MediaBrowserCompat,它在 Media-Compat 支持库中定义。在本页中,术语“MediaBrowser”是指 MediaBrowserCompat 的一个实例。

连接到 MediaBrowserService

创建客户端 Activity 后,它会连接到 MediaBrowserService。这里涉及一点握手和跳跃。修改 Activity 的生命周期回调,如下所示:

  • onCreate() 构造 MediaBrowserCompat。传入您的 MediaBrowserService 的名称和您已定义的 MediaBrowserCompat.ConnectionCallback。
  • onStart() 连接到 MediaBrowserService。这里体现了 MediaBrowserCompat.ConnectionCallback 的神奇之处。如果连接成功,onConnect() 回调会创建媒体控制器,将其链接到媒体会话,将您的界面控件链接到 MediaController,并注册控制器以接收来自媒体会话的回调。
  • onResume() 设置音频流,以便您的应用响应设备上的音量控制。
  • onStop() 断开 MediaBrowser 的连接,并在 Activity 停止时取消注册 MediaController.Callback。

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();

      }
    }
    

自定义 MediaBrowserCompat.ConnectionCallback

当您的 Activity 构造 MediaBrowserCompat 时,您必须创建 ConnectionCallback 的实例。修改其 onConnected() 方法以从 MediaBrowserService 检索媒体会话令牌,并使用该令牌创建 MediaControllerCompat。

使用便捷方法 MediaControllerCompat.setMediaController() 保存指向控制器的链接,以便处理媒体按钮。您还可利用此方法在构建传输控件时调用 MediaControllerCompat.getMediaController() 来检索控制器。

以下代码示例演示了如何修改 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
        }
      };
    

将您的界面连接到媒体控制器

在上面的 ConnectionCallback 示例代码中,包含对 buildTransportControls() 的调用以充实您的界面。您需要为控制播放器的界面元素设置 onClickListeners。为每一个元素选择合适的 MediaControllerCompat.TransportControls 方法。

您的代码将如下所示,每个按钮都有一个 onClickListener:

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);
    }
    }
    

TransportControls 方法向您的服务的媒体会话发送回调。确保您为每个控件定义了相应的 MediaSessionCompat.Callback 方法。

与媒体会话保持同步

界面应显示媒体会话的当前状态(通过其 PlaybackState 和元数据来描述)。创建传输控件时,您可以抓取会话的当前状态,将其显示在界面中,并根据状态及其可用操作启用和停用传输控件。

要在媒体会话的状态或元数据每次发生更改时从媒体会话接收回调,请使用以下两种方法定义 MediaControllerCompat.Callback

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) {}
      };
    

在构建传输控件时注册回调(请参阅 buildTransportControls() 方法),并在 Activity 停止时(在 Activity 的 onStop() 生命周期方法中)取消注册回调。