Медиасеансы позволяют взаимодействовать с аудио- и видеопроигрывателями. В Media3 проигрыватель по умолчанию – это класс ExoPlayer, который реализует интерфейс Player. Подключение медиасеанса к проигрывателю позволяет приложению рекламировать воспроизведение медиаконтента внешним образом и получать команды воспроизведения из внешних источников.
Команды могут поступать от физических кнопок, например кнопки воспроизведения на наушниках или пульте дистанционного управления телевизора. Также они могут поступать от клиентских приложений с медиаконтроллером, например когда вы просите Google Ассистента поставить воспроизведение на паузу. Медиасессия передает эти команды проигрывателю медиаприложения.
Когда нужно выбирать сеанс мультимедиа
При реализации MediaSession пользователи могут управлять воспроизведением:
- Через наушники. На наушниках часто есть кнопки или сенсорные панели, с помощью которых можно воспроизводить или приостанавливать медиаконтент, а также переходить к следующему или предыдущему треку.
- Поговорите с Google Ассистентом. Чаще всего пользователи говорят Окей, Google, приостанови, чтобы приостановить воспроизведение медиаконтента на устройстве.
- С помощью часов Wear OS. Это позволяет легко управлять воспроизведением на телефоне.
- С помощью элементов управления мультимедиа. На этой карусели показываются элементы управления для каждого запущенного сеанса воспроизведения медиаконтента.
- На телевизоре. Позволяет выполнять действия с помощью физических кнопок воспроизведения, элементов управления воспроизведением на платформе и функций управления питанием (например, если телевизор, саундбар или AV-ресивер выключается или переключается вход, воспроизведение в приложении должно остановиться).
- С помощью элементов управления мультимедиа в Android Auto. Это позволяет безопасно управлять воспроизведением во время вождения.
- и другие внешние процессы, которые должны влиять на воспроизведение.
Это удобно во многих случаях. В частности, мы рекомендуем использовать MediaSession, если:
- Вы транслируете длинный видеоконтент, например фильмы или онлайн-телевидение.
- Вы транслируете аудиоконтент большой продолжительности, например подкасты или музыкальные плейлисты.
- Вы создаете ТВ-приложение.
Однако не все варианты использования хорошо подходят для MediaSession. Использовать только значок Player может быть удобно в следующих случаях:
- Вы показываете короткие видео, для которых не требуется внешний элемент управления или воспроизведение в фоновом режиме.
- Нет одного активного видео, например пользователь прокручивает список, и на экране одновременно показываются несколько видео.
- Вы показываете одноразовое вводное или поясняющее видео, которое пользователь должен активно смотреть, не используя внешние элементы управления воспроизведением.
- Ваш контент конфиденциален, и вы не хотите, чтобы внешние процессы получали доступ к метаданным медиаконтента (например, в режиме инкогнито в браузере).
Если ваш вариант использования не соответствует ни одному из перечисленных выше, подумайте, допустимо ли для вас, чтобы приложение продолжало воспроизводить контент, когда пользователь не взаимодействует с ним. Если да, то, скорее всего, вам подойдет вариант MediaSession. Если нет, то, скорее всего, вам нужно использовать свойство Player.
Как создать сеанс мультимедиа
Медиасеанс существует вместе с проигрывателем, которым он управляет. Вы можете создать сеанс работы с медиаконтентом с помощью объектов Context и Player. Создавать и инициализировать сеанс мультимедиа следует, когда это необходимо, например в методе жизненного цикла onStart() или onResume() класса Activity или Fragment или в методе onCreate() класса Service, которому принадлежит сеанс мультимедиа и связанный с ним проигрыватель.
Чтобы создать сеанс мультимедиа, инициализируйте Player и передайте его в MediaSession.Builder следующим образом:
Kotlin
val player = ExoPlayer.Builder(context).build() val mediaSession = MediaSession.Builder(context, player).build()
Java
ExoPlayer player = new ExoPlayer.Builder(context).build(); MediaSession mediaSession = new MediaSession.Builder(context, player).build();
Автоматическая обработка состояний
Библиотека Media3 автоматически обновляет сеанс мультимедиа, используя состояние проигрывателя. Поэтому вам не нужно вручную сопоставлять проигрыватель с сеансом.
Это отличается от медиасеанса платформы, в котором вам нужно было создать и поддерживать PlaybackState независимо от самого проигрывателя, например чтобы указать на ошибки.
Уникальный идентификатор сеанса
По умолчанию MediaSession.Builder создает сеанс с пустой строкой в качестве идентификатора сеанса. Этого достаточно, если приложение предназначено для создания только одного экземпляра сеанса, что является наиболее распространенным случаем.
Если приложению нужно управлять несколькими экземплярами сеанса одновременно, оно должно обеспечить уникальность идентификатора каждого сеанса. Идентификатор сеанса можно задать при создании сеанса с помощью MediaSession.Builder.setId(String id).
Если вы видите, что IllegalStateException приводит к сбою приложения с сообщением об ошибке IllegalStateException: Session ID must be unique. ID=, то, скорее всего, сеанс был неожиданно создан до того, как был освобожден ранее созданный экземпляр с тем же идентификатором. Чтобы избежать утечки сеансов из-за ошибки программирования, такие случаи обнаруживаются и о них сообщается с помощью исключения.
Как передать управление другим клиентам
Сеанс мультимедиа – это ключ к управлению воспроизведением. Он позволяет направлять команды из внешних источников проигрывателю, который воспроизводит ваш медиаконтент. Это могут быть физические кнопки, например кнопка воспроизведения на гарнитуре или пульте ДУ телевизора, или косвенные команды, например "приостановить" для Google Ассистента. Вы также можете предоставить доступ к системе Android, чтобы управлять уведомлениями и элементами управления заблокированного экрана, или к часам Wear OS, чтобы управлять воспроизведением с циферблата. Внешние клиенты могут использовать медиаконтроллер, чтобы отправлять команды воспроизведения в ваше мультимедийное приложение. Эти команды получает медиасессия, которая затем передает их медиапроигрывателю.
Когда контроллер собирается подключиться к сеансу мультимедиа, вызывается метод onConnect(). Вы можете использовать предоставленный ControllerInfo, чтобы решить, принять или отклонить запрос. Пример того, как принять запрос на подключение, приведен в разделе Как объявить специальные команды.
После подключения контроллер может отправлять команды воспроизведения в сеанс. Затем сеанс делегирует эти команды проигрывателю. Команды воспроизведения и плейлистов, определенные в интерфейсе Player, автоматически обрабатываются сеансом.
Другие методы обратного вызова позволяют обрабатывать, например, запросы на пользовательские команды и изменение плейлиста. Эти функции обратного вызова также включают объект ControllerInfo, поэтому вы можете изменять способ обработки каждого запроса для каждого контроллера.
Как изменить плейлист
Медиасессия может напрямую изменять плейлист своего проигрывателя, как описано в руководстве по плейлистам ExoPlayer.
Контролеры также могут изменять плейлист, если у них есть доступ к значку COMMAND_SET_MEDIA_ITEM или COMMAND_CHANGE_MEDIA_ITEMS.
При добавлении новых объектов в плейлист проигрывателю обычно требуется MediaItem экземпляров с определенным URI, чтобы их можно было воспроизвести. По умолчанию новые добавленные объекты автоматически пересылаются в методы проигрывателя, например player.addMediaItem, если для них задан URI.
Если вы хотите настроить экземпляры MediaItem, добавленные в проигрыватель, вы можете переопределить onAddMediaItems().
Этот шаг необходим, если вы хотите поддерживать контроллеры, которые запрашивают медиаконтент без определенного URI. Вместо этого в MediaItem обычно задаются одно или несколько следующих полей, описывающих запрошенный медиаконтент:
MediaItem.id: универсальный идентификатор медиаконтента.MediaItem.RequestMetadata.mediaUri– URI запроса, в котором может использоваться специальная схема и который не обязательно можно воспроизвести напрямую.MediaItem.RequestMetadata.searchQuery– текстовый поисковый запрос, например от Google Ассистента.MediaItem.MediaMetadata– структурированные метаданные, например название или исполнитель.
Чтобы задать дополнительные настройки для новых плейлистов, вы можете переопределить параметр onSetMediaItems(), который позволяет указать начальный элемент и его позицию в плейлисте. Например, можно развернуть один запрошенный элемент до целого списка воспроизведения и указать проигрывателю начать с индекса изначально запрошенного элемента. Пример реализации onSetMediaItems() с этой функцией можно найти в демонстрационном приложении для сеансов.
Как управлять настройками кнопок управления мультимедиа
Каждый контроллер, например интерфейс системы, Android Auto или Wear OS, может самостоятельно решать, какие кнопки показывать пользователю. Чтобы указать, какие элементы управления воспроизведением вы хотите показывать пользователю, задайте предпочтения для кнопок мультимедиа в MediaSession. Эти настройки представляют собой упорядоченный список экземпляров CommandButton, каждый из которых определяет предпочтение для кнопки в пользовательском интерфейсе.
Как задать кнопки команд
Экземпляры CommandButton используются для определения настроек кнопок управления мультимедиа. Каждая кнопка определяет три аспекта нужного элемента интерфейса:
- Значок, определяющий внешний вид. При создании
CommandButton.Builderзначок должен быть задан как одна из предопределенных констант. Обратите внимание, что это не реальный ресурс Bitmap или изображения. Универсальная константа помогает контроллерам выбирать подходящий ресурс для обеспечения единообразного внешнего вида в собственном интерфейсе. Если ни одна из предопределенных констант значков не подходит для вашего случая, вы можете использоватьsetCustomIconResId. - Команда, определяющая действие, которое будет выполнено, когда пользователь нажмет кнопку. Вы можете использовать
setPlayerCommandдляPlayer.CommandилиsetSessionCommandдля стандартного или пользовательскогоSessionCommand. - Слот, определяющий, где на контроллере должна быть размещена кнопка. Это необязательное поле, которое заполняется автоматически на основе значка и команды. Например, можно указать, что кнопка должна отображаться в области навигации "Вперед" вместо области "Ещё".
Kotlin
val button = CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD_15) .setPlayerCommand(Player.COMMAND_SEEK_FORWARD) .setSlots(CommandButton.SLOT_FORWARD) .build()
Java
CommandButton button = new CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD_15) .setPlayerCommand(Player.COMMAND_SEEK_FORWARD) .setSlots(CommandButton.SLOT_FORWARD) .build();
При определении настроек кнопок управления мультимедиа применяется следующий алгоритм:
- Для каждого элемента
CommandButtonв настройках кнопок управления медиаконтентом поместите кнопку в первый доступный и разрешенный слот. - Если в каком-либо из центральных, передних или задних слотов нет кнопки, добавьте для него кнопку по умолчанию.
Вы можете использовать CommandButton.DisplayConstraints, чтобы посмотреть, как будут работать кнопки управления мультимедиа в зависимости от ограничений интерфейса.
Как настроить параметры кнопок управления мультимедиа
Самый простой способ задать параметры кнопки мультимедиа – определить список при создании MediaSession. Вы также можете переопределить кнопку MediaSession.Callback.onConnect, чтобы настроить параметры кнопок управления мультимедиа для каждого подключенного контроллера.
Kotlin
val mediaSession = MediaSession.Builder(context, player) .setMediaButtonPreferences(ImmutableList.of(likeButton, favoriteButton)) .build()
Java
MediaSession mediaSession = new MediaSession.Builder(context, player) .setMediaButtonPreferences(ImmutableList.of(likeButton, favoriteButton)) .build();
Как изменить настройки кнопок управления мультимедиа после взаимодействия пользователя
После того как пользователь взаимодействует с проигрывателем, вы можете обновить кнопки, которые показываются в интерфейсе контроллера. Типичный пример – кнопка-переключатель, которая меняет значок и действие после того, как будет выполнено связанное с ней действие. Чтобы изменить настройки кнопки мультимедиа, используйте MediaSession.setMediaButtonPreferences, чтобы обновить настройки для всех контроллеров или определенного контроллера:
Kotlin
// Handle "favoritesButton" action, replace by opposite button mediaSession.setMediaButtonPreferences(ImmutableList.of(likeButton, removeFromFavoritesButton))
Java
// Handle "favoritesButton" action, replace by opposite button mediaSession.setMediaButtonPreferences(ImmutableList.of(likeButton, removeFromFavoritesButton));
Как добавить специальные команды и настроить поведение по умолчанию
Доступные команды проигрывателя можно расширить с помощью специальных команд. Кроме того, можно перехватывать входящие команды проигрывателя и нажатия кнопок управления мультимедиа, чтобы изменить поведение по умолчанию.
Как объявлять и обрабатывать специальные команды
Медиаприложения могут определять специальные команды, которые, например, можно использовать в настройках кнопок управления мультимедиа. Например, вы можете добавить кнопки, с помощью которых пользователь сможет сохранять медиаконтент в список избранного. MediaController отправляет команды, а MediaSession.Callback их получает.
Чтобы задать специальные команды, необходимо переопределить MediaSession.Callback.onConnect(), чтобы задать доступные специальные команды для каждого подключенного контроллера.
Kotlin
private class CustomMediaSessionCallback : MediaSession.Callback { // Configure commands available to the controller in onConnect() override fun onConnectAsync( session: MediaSession, controller: ControllerInfo, ): ListenableFuture<ConnectionResult> { val sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() .add(SessionCommand(SAVE_TO_FAVORITES, Bundle.EMPTY)) .build() return Futures.immediateFuture( AcceptedResultBuilder(session, controller) .setAvailableSessionCommands(sessionCommands) .build() ) } }
Java
private static class CustomMediaSessionCallback implements MediaSession.Callback { // Configure commands available to the controller in onConnect() @Override public ListenableFuture<ConnectionResult> onConnectAsync( MediaSession session, ControllerInfo controller) { SessionCommands sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS .buildUpon() .add(new SessionCommand(SAVE_TO_FAVORITES, new Bundle())) .build(); return Futures.immediateFuture( new AcceptedResultBuilder(session, controller) .setAvailableSessionCommands(sessionCommands) .build()); } }
Чтобы получать запросы на выполнение специальных команд от MediaController, переопределите метод onCustomCommand() в Callback.
Kotlin
private class CustomCallback : MediaSession.Callback { // ... override fun onCustomCommand( session: MediaSession, controller: ControllerInfo, customCommand: SessionCommand, args: Bundle, ): ListenableFuture<SessionResult> { if (customCommand.customAction == SAVE_TO_FAVORITES) { // Do custom logic here saveToFavorites(session.player.currentMediaItem) return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS)) } // ... return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS)) } }
Java
private static class CustomCallback implements MediaSession.Callback { // ... @Override public ListenableFuture<SessionResult> onCustomCommand( MediaSession session, ControllerInfo controller, SessionCommand customCommand, Bundle args) { if (customCommand.customAction.equals(SAVE_TO_FAVORITES)) { // Do custom logic here saveToFavorites(session.getPlayer().getCurrentMediaItem()); return Futures.immediateFuture(new SessionResult(SessionResult.RESULT_SUCCESS)); } // ... return Futures.immediateFuture(new SessionResult(SessionResult.RESULT_SUCCESS)); } }
Вы можете отслеживать, какой контроллер мультимедиа отправляет запрос, с помощью свойства packageName объекта MediaSession.ControllerInfo, который передается в методы Callback. Это позволяет настроить поведение приложения в ответ на определенную команду, если она исходит от системы, вашего приложения или других клиентских приложений.
Как настроить команды для проигрывателя по умолчанию
Все команды по умолчанию и обработка состояний делегируются Player, который находится на MediaSession. Чтобы настроить поведение команды, определенной в интерфейсе Player, например play() или seekToNext(), оберните Player в ForwardingSimpleBasePlayer, прежде чем передавать его в MediaSession:
Kotlin
val forwardingPlayer = object : ForwardingSimpleBasePlayer(player) { // Customizations } val mediaSession = MediaSession.Builder(context, forwardingPlayer).build()
Java
ForwardingSimpleBasePlayer forwardingPlayer = new ForwardingSimpleBasePlayer(player) { // Customizations }; MediaSession mediaSession = new MediaSession.Builder(context, forwardingPlayer).build();
Подробнее о ForwardingSimpleBasePlayer рассказано в руководстве по ExoPlayer в разделе Настройка.
Как определить контроллер, отправивший команду проигрывателю
Если метод Player вызывается из MediaController, вы можете определить источник с помощью MediaSession.controllerForCurrentRequest и получить ControllerInfo для текущего запроса:
Kotlin
private class CallerAwarePlayer(player: Player) : ForwardingSimpleBasePlayer(player) { private lateinit var session: MediaSession override fun handleSeek( mediaItemIndex: Int, positionMs: Long, seekCommand: Int, ): ListenableFuture<*> { Log.d( "caller", "seek operation from package ${session.controllerForCurrentRequest?.packageName}", ) return super.handleSeek(mediaItemIndex, positionMs, seekCommand) } }
Java
private static final class CallerAwarePlayer extends ForwardingSimpleBasePlayer { private MediaSession session; public CallerAwarePlayer(Player player) { super(player); } @Override protected ListenableFuture<?> handleSeek(int mediaItemIndex, long positionMs, int seekCommand) { Log.d( "caller", "seek operation from package: " + session.getControllerForCurrentRequest().getPackageName()); return super.handleSeek(mediaItemIndex, positionMs, seekCommand); } }
Как настроить обработку нажатий на кнопки управления мультимедиа
Кнопки управления мультимедиа – это аппаратные кнопки на устройствах Android и других периферийных устройствах, например кнопка воспроизведения/паузы на Bluetooth-гарнитуре. Media3 обрабатывает события нажатия кнопок управления медиаконтентом, когда они поступают в сеанс, и вызывает соответствующий метод Player в проигрывателе сеанса.
Рекомендуется обрабатывать все входящие события нажатия кнопки мультимедиа в соответствующем методе Player. В более сложных случаях события кнопки мультимедиа можно перехватывать в MediaSession.Callback.onMediaButtonEvent(Intent).
Обработка ошибок и создание отчетов
Сеанс может выдать два типа ошибок и сообщить о них контроллерам. Критическая ошибка означает, что в работе проигрывателя произошел технический сбой, из-за которого воспроизведение было прервано. Критические ошибки автоматически передаются контроллеру при их возникновении. Некритические ошибки – это ошибки, связанные с правилами или нетехническими проблемами, которые не прерывают воспроизведение. Приложение отправляет их контроллерам вручную.
Критические ошибки воспроизведения
Критическая ошибка воспроизведения передается в сеанс проигрывателем, а затем контроллерам, чтобы вызвать Player.Listener.onPlayerError(PlaybackException) и Player.Listener.onPlayerErrorChanged(@Nullable PlaybackException).
В таком случае состояние воспроизведения меняется на STATE_IDLE, а MediaController.getPlaybackError() возвращает PlaybackException, из-за которого произошел переход. Контроллер может проверить PlayerException.errorCode, чтобы узнать причину ошибки.
Как задать специальную ошибку проигрывателя
Помимо критических ошибок, сообщаемых проигрывателем, приложение может задать специальный код ошибки PlaybackException на уровне MediaSession с помощью MediaSession.setPlaybackException(PlaybackException). Это позволяет приложению сообщать подключенным контроллерам о состоянии ошибки. Исключение можно задать для всех подключенных контроллеров или для определенного ControllerInfo.
Когда приложение задает PlaybackException с помощью этого API:
Подключенные экземпляры
MediaControllerполучат уведомление. Обратные вызовыListener.onPlayerError(PlaybackException)иListener.onPlayerErrorChanged(@Nullable PlaybackException)на контроллере будут вызваны с предоставленным исключением.Метод
MediaController.getPlayerError()вернет значениеPlaybackException, заданное приложением.Статус воспроизведения на затронутых контроллерах изменится на
Player.STATE_IDLE.Доступные команды будут удалены, и останутся только команды для чтения, например
COMMAND_GET_TIMELINE, если они уже были предоставлены. СостояниеTimeline, например, заморожено на том состоянии, в котором исключение было применено к контроллеру. Команды, которые пытаются изменить состояние проигрывателя, напримерCOMMAND_PLAY, удаляются, пока приложение не снимет исключение для воспроизведения для контроллера.
Чтобы удалить заданное ранее значение PlaybackException и восстановить обычный режим передачи данных о состоянии проигрывателя, приложение может вызвать метод MediaSession.setPlaybackException(/* playbackException= */ null) или MediaSession.setPlaybackException(ControllerInfo,
/* playbackException= */ null).
Настройка критических ошибок
Чтобы предоставить пользователю локализованную и полезную информацию, вы можете настроить код ошибки, сообщение об ошибке и дополнительные данные об ошибке воспроизведения, полученные от проигрывателя. Для этого при создании сеанса используйте объект ForwardingPlayer:
Kotlin
val session = MediaSession.Builder(context, ErrorForwardingPlayer(context, player)).build()
Java
MediaSession session = new MediaSession.Builder(context, new ErrorForwardingPlayer(context, player)).build();
Пересылающий проигрыватель может использовать ForwardingSimpleBasePlayer, чтобы перехватить ошибку и изменить ее код, сообщение или дополнительные данные. Аналогичным образом можно создавать новые ошибки, которых нет в исходном проигрывателе:
Kotlin
private class ErrorForwardingPlayer(private val context: Context, player: Player) : ForwardingSimpleBasePlayer(player) { override fun getState(): State { var state = super.getState() if (state.playerError != null) { state = state.buildUpon().setPlayerError(customizePlaybackException(state.playerError!!)).build() } return state } private fun customizePlaybackException(error: PlaybackException): PlaybackException { val buttonLabel: String val errorMessage: String when (error.errorCode) { PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW -> { buttonLabel = context.getString(R.string.err_button_label_restart_stream) errorMessage = context.getString(R.string.err_msg_behind_live_window) } else -> { buttonLabel = context.getString(R.string.err_button_label_ok) errorMessage = context.getString(R.string.err_message_default) } } val extras = Bundle() extras.putString("button_label", buttonLabel) return PlaybackException(errorMessage, error.cause, error.errorCode, extras) } }
Java
private static class ErrorForwardingPlayer extends ForwardingSimpleBasePlayer { private final Context context; public ErrorForwardingPlayer(Context context, Player player) { super(player); this.context = context; } @Override protected State getState() { State state = super.getState(); if (state.playerError != null) { state = state.buildUpon().setPlayerError(customizePlaybackException(state.playerError)).build(); } return state; } private PlaybackException customizePlaybackException(PlaybackException error) { String buttonLabel; String errorMessage; switch (error.errorCode) { case PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW: buttonLabel = context.getString(R.string.err_button_label_restart_stream); errorMessage = context.getString(R.string.err_msg_behind_live_window); break; default: buttonLabel = context.getString(R.string.err_button_label_ok); errorMessage = context.getString(R.string.err_message_default); break; } Bundle extras = new Bundle(); extras.putString("button_label", buttonLabel); return new PlaybackException(errorMessage, error.getCause(), error.errorCode, extras); } }
Некритические ошибки
Нефатальные ошибки, которые не связаны с техническими исключениями, могут быть отправлены приложением всем или определенным контроллерам:
Kotlin
val sessionError = SessionError( SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED, context.getString(R.string.error_message_authentication_expired), ) // Option 1: Sending a nonfatal error to all controllers. mediaSession.sendError(sessionError) // Option 2: Sending a nonfatal error to the media notification controller only // to set the error code and error message in the playback state of the platform // media session. mediaSession.mediaNotificationControllerInfo?.let { mediaSession.sendError(it, sessionError) }
Java
SessionError sessionError = new SessionError( SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED, context.getString(R.string.error_message_authentication_expired)); // Option 1: Sending a nonfatal error to all controllers. mediaSession.sendError(sessionError); // Option 2: Sending a nonfatal error to the media notification controller only // to set the error code and error message in the playback state of the platform // media session. ControllerInfo mediaNotificationControllerInfo = mediaSession.getMediaNotificationControllerInfo(); if (mediaNotificationControllerInfo != null) { mediaSession.sendError(mediaNotificationControllerInfo, sessionError); }
Если контроллер уведомлений о медиаконтенте получает сообщение о некритической ошибке, код и текст ошибки копируются в медиасеанс платформы, а значение PlaybackState.state не меняется на STATE_ERROR.
Получение некритических ошибок
MediaController получает некритическую ошибку, реализовав MediaController.Listener.onError:
Kotlin
val future = MediaController.Builder(context, sessionToken) .setListener( object : MediaController.Listener { override fun onError(controller: MediaController, sessionError: SessionError) { // Handle nonfatal error. } } ) .buildAsync()
Java
MediaController.Builder future = new MediaController.Builder(context, sessionToken) .setListener( new MediaController.Listener() { @Override public void onError(MediaController controller, SessionError sessionError) { // Handle nonfatal error. } });