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

Чтобы завершить проектирование клиента/сервера, вам необходимо создать компонент активности, содержащий код пользовательского интерфейса, связанный 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
   
}
 
};