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

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

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

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

Подключение к MediaBrowserService

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

  • onCreate() создает MediaBrowserCompat. Передайте название MediaBrowserService и MediaBrowserCompat.ConnectionCallback, которые вы определили.
  • onStart() подключается к MediaBrowserService. Здесь и пригодится MediaBrowserCompat.ConnectionCallback. Если подключение установлено, обратный вызов onConnect() создает контроллер мультимедиа, связывает его с сеансом мультимедиа, связывает элементы управления пользовательского интерфейса с MediaController и регистрирует контроллер для получения обратных вызовов от сеанса мультимедиа.
  • onResume() настраивает аудиопоток так, чтобы приложение реагировало на регулировку громкости на устройстве.
  • onStop() отключает MediaBrowser и отменяет регистрацию 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

Когда ваше действие создает 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 и Metadata. При создании элементов управления воспроизведением вы можете получить текущее состояние сеанса, отобразить его в пользовательском интерфейсе, а также включить или отключить элементы управления воспроизведением в зависимости от состояния и доступных действий.

Чтобы получать обратные вызовы от сеанса мультимедиа каждый раз, когда меняется его состояние или метаданные, определите 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()) и отмените регистрацию, когда действие остановится (в методе жизненного цикла onStop()).

Отключение при завершении сеанса воспроизведения медиаконтента

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

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

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

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