Создание клиента медиабраузера

Чтобы завершить проектирование клиента/сервера, вам необходимо создать компонент действия, содержащий код пользовательского интерфейса, связанный MediaController и MediaBrowser.

MediaBrowser выполняет две важные функции: он подключается к MediaBrowserService и после подключения создает MediaController для вашего пользовательского интерфейса.

Примечание. Рекомендуемой реализацией MediaBrowser является MediaBrowserCompat , которая определена в библиотеке поддержки Media-Compat . На этой странице термин «MediaBrowser» относится к экземпляру MediaBrowserCompat.

Подключитесь к MediaBrowserService.

Когда ваша клиентская активность создается, она подключается к MediaBrowserService. Это небольшое рукопожатие и танец. Измените обратные вызовы жизненного цикла действия следующим образом:

  • onCreate() создает MediaBrowserCompat. Передайте имя вашего MediaBrowserService и определенный вами MediaBrowserCompat.ConnectionCallback.
  • onStart() подключается к MediaBrowserService. Вот здесь-то и проявляется магия MediaBrowserCompat.ConnectionCallback. Если соединение установлено успешно, обратный вызов onConnect() создает медиа-контроллер, связывает его с медиа-сеансом, связывает ваши элементы управления пользовательского интерфейса с MediaController и регистрирует контроллер для получения обратных вызовов от медиа-сессия.
  • onResume() устанавливает аудиопоток, чтобы ваше приложение реагировало на регулятор громкости на устройстве.
  • onStop() отключает ваш MediaBrowser и отменяет регистрацию MediaController.Callback, когда ваша активность прекращается.

Котлин

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

  }
}

Настройка MediaBrowserCompat.ConnectionCallback

Когда ваша деятельность создает MediaBrowserCompat, вы должны создать экземпляр ConnectionCallback. Измените его метод onConnected() , чтобы получить токен медиа-сеанса из MediaBrowserService, и используйте этот токен для создания MediaControllerCompat.

Используйте удобный метод MediaControllerCompat.setMediaController() чтобы сохранить ссылку на контроллер. Это позволяет обрабатывать мультимедийные кнопки . Это также позволяет вам вызывать MediaControllerCompat.getMediaController() для получения контроллера при создании элементов управления транспортом.

В следующем примере кода показано, как изменить метод 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
    }
  };

Подключите свой пользовательский интерфейс к медиаконтроллеру

В приведенном выше примере кода ConnectionCallback включен вызов buildTransportControls() для конкретизации вашего пользовательского интерфейса. Вам нужно будет установить onClickListeners для элементов пользовательского интерфейса, которые управляют проигрывателем. Выберите соответствующий метод MediaControllerCompat.TransportControls для каждого из них.

Ваш код будет выглядеть примерно так, с onClickListener для каждой кнопки:

Котлин

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

Методы TransportControls отправляют обратные вызовы в сеанс мультимедиа вашей службы. Убедитесь, что вы определили соответствующий метод MediaSessionCompat.Callback для каждого элемента управления.

Оставайтесь в курсе медиа-сессии

Пользовательский интерфейс должен отображать текущее состояние сеанса мультимедиа, как описано в его PlaybackState и метаданных. При создании элементов управления транспортом вы можете получить текущее состояние сеанса, отобразить его в пользовательском интерфейсе, а также включить и отключить элементы управления транспортом на основе состояния и доступных действий.

Чтобы получать обратные вызовы из медиа-сеанса каждый раз, когда его состояние или метаданные изменяются, определите MediaControllerCompat.Callback с помощью этих двух методов:

Котлин

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

Зарегистрируйте обратный вызов при создании элементов управления транспортом (см. метод buildTransportControls() ) и отмените его регистрацию при остановке действия (в методе жизненного цикла действия onStop() ).

Отключиться при уничтожении медиа-сессии

Если медиа-сеанс становится недействительным, выдается обратный вызов onSessionDestroyed() . Когда это произойдет, сеанс не сможет снова стать функциональным в течение срока службы MediaBrowserService . Хотя функции, связанные с MediaBrowser могут продолжать работать, пользователь не может просматривать или управлять воспроизведением разрушенного мультимедийного сеанса, что, скорее всего, снизит ценность вашего приложения.

Поэтому, когда сеанс уничтожается, вы должны отключиться от MediaBrowserService , вызвав метод disconnect() . Это гарантирует, что служба браузера не имеет связанных клиентов и может быть уничтожена ОС . Если вам потребуется повторно подключиться к MediaBrowserService позже (например, если ваше приложение хочет поддерживать постоянное соединение с мультимедийным приложением), создайте новый экземпляр MediaBrowser , а не повторно используйте старый.

Следующий фрагмент кода демонстрирует реализацию обратного вызова, который отключается от службы браузера при уничтожении медиа-сеанса:

Котлин

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