Создайте базовое приложение медиаплеера с помощью Media3 ExoPlayer.

Jetpack Media3 определяет интерфейс Player , в котором описываются основные функции воспроизведения видео и аудио файлов. ExoPlayer является реализацией этого интерфейса по умолчанию в Media3. Мы рекомендуем использовать ExoPlayer, поскольку он предоставляет полный набор функций, которые охватывают большинство вариантов использования воспроизведения, и его можно настроить для любых дополнительных вариантов использования, которые могут у вас возникнуть. ExoPlayer также абстрагирует фрагментацию устройств и ОС, поэтому ваш код работает согласованно во всей экосистеме Android. ExoPlayer включает в себя:

На этой странице описаны некоторые ключевые этапы создания приложения для воспроизведения, а для получения более подробной информации вы можете обратиться к нашим полным руководствам по Media3 ExoPlayer .

Начиная

Для начала добавьте зависимость от модулей ExoPlayer, UI и Common Jetpack Media3:

implementation "androidx.media3:media3-exoplayer:1.4.1"
implementation "androidx.media3:media3-ui:1.4.1"
implementation "androidx.media3:media3-common:1.4.1"

В зависимости от вашего варианта использования вам также могут понадобиться дополнительные модули из Media3, такие как exoplayer-dash для воспроизведения потоков в формате DASH.

Обязательно замените 1.4.1 предпочитаемой версией библиотеки. Вы можете обратиться к примечаниям к выпуску , чтобы увидеть последнюю версию.

Создание медиаплеера

С Media3 вы можете использовать встроенную реализацию интерфейса Player ExoPlayer или создать свою собственную реализацию.

Создание ExoPlayer

Самый простой способ создать экземпляр ExoPlayer заключается в следующем:

Котлин

val player = ExoPlayer.Builder(context).build()

Ява

ExoPlayer player = new ExoPlayer.Builder(context).build();

Вы можете создать свой медиаплеер в методе жизненного цикла onCreate() Activity , Fragment или Service , где он находится.

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

Media3 предоставляет компонент пользовательского интерфейса PlayerView , который вы можете включить в файл макета вашего приложения. Этот компонент инкапсулирует PlayerControlView для элементов управления воспроизведением, SubtitleView для отображения субтитров и Surface для рендеринга видео.

Подготовка игрока

Добавьте медиа-элементы в список воспроизведения для воспроизведения с помощью таких методов, как setMediaItem() и addMediaItem() . Затем вызовите prepare() , чтобы начать загрузку носителя и получить необходимые ресурсы.

Не следует выполнять эти действия, пока приложение не окажется на переднем плане. Если ваш проигрыватель находится в Activity или Fragment , это означает подготовку проигрывателя с помощью метода жизненного цикла onStart() на уровне API 24 и выше или метода жизненного цикла onResume() на уровне API 23 и ниже. Для игрока, находящегося в Service , вы можете подготовить его в onCreate() .

Управляйте игроком

После подготовки проигрывателя вы можете управлять воспроизведением, вызывая такие методы проигрывателя, как:

Компоненты пользовательского интерфейса, такие как PlayerView или PlayerControlView будут обновляться соответствующим образом при привязке к игроку.

Отпустить плеер

Для воспроизведения могут потребоваться ресурсы, количество которых ограничено, например видеодекодеры, поэтому важно вызывать release() на проигрывателе, чтобы освободить ресурсы, когда проигрыватель больше не нужен.

Если ваш проигрыватель находится в Activity или Fragment , освободите его с помощью метода жизненного цикла onStop() на уровне API 24 и выше или метода onPause() на уровне API 23 и ниже. Для игрока, находящегося в Service , вы можете освободить его с помощью onDestroy() .

Управление воспроизведением с помощью медиасеанса

В Android медиасеансы предоставляют стандартизированный способ взаимодействия с медиаплеером, невзирая на границы процесса. Подключение медиа-сеанса к вашему проигрывателю позволяет вам рекламировать воспроизведение мультимедиа извне и получать команды воспроизведения из внешних источников, например, для интеграции с системными элементами управления мультимедиа на мобильных устройствах и устройствах с большим экраном.

Чтобы использовать медиа-сессии, добавьте зависимость к модулю Media3 Session:

implementation "androidx.media3:media3-session:1.4.1"

Создать медиа-сессию

Вы можете создать MediaSession после инициализации проигрывателя следующим образом:

Котлин

val player = ExoPlayer.Builder(context).build()
val mediaSession = MediaSession.Builder(context, player).build()

Ява

ExoPlayer player = new ExoPlayer.Builder(context).build();
MediaSession mediaSession = new MediaSession.Builder(context, player).build();

Media3 автоматически синхронизирует состояние Player с состоянием MediaSession . Это работает с любой реализацией Player , включая ExoPlayer , CastPlayer или пользовательскую реализацию.

Предоставить контроль другим клиентам

Клиентские приложения могут реализовать медиа-контроллер для управления воспроизведением вашего медиа-сеанса. Чтобы получать эти запросы, установите объект обратного вызова при создании MediaSession .

Когда контроллер собирается подключиться к вашему медиа-сеансу, вызывается метод onConnect() . Вы можете использовать предоставленную ControllerInfo чтобы решить, принять или отклонить запрос. См. пример этого в демонстрационном приложении Media3 Session .

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

Другие методы обратного вызова позволяют обрабатывать, например, запросы на пользовательские команды воспроизведения и изменение списка воспроизведения . Эти обратные вызовы также включают объект ControllerInfo , поэтому вы можете определять управление доступом для каждого запроса.

Воспроизведение мультимедиа в фоновом режиме

Чтобы продолжить воспроизведение мультимедиа, когда ваше приложение не находится на переднем плане, например для воспроизведения музыки, аудиокниг или подкастов, даже если у пользователя не открыто ваше приложение, ваш Player и MediaSession должны быть инкапсулированы в службу переднего плана . Для этой цели Media3 предоставляет интерфейс MediaSessionService .

Реализация MediaSessionService

Создайте класс, расширяющий MediaSessionService , и создайте экземпляр MediaSession в методе жизненного цикла onCreate() .

Котлин

class PlaybackService : MediaSessionService() {
    private var mediaSession: MediaSession? = null

    // Create your Player and MediaSession in the onCreate lifecycle event
    override fun onCreate() {
        super.onCreate()
        val player = ExoPlayer.Builder(this).build()
        mediaSession = MediaSession.Builder(this, player).build()
    }

    // Remember to release the player and media session in onDestroy
    override fun onDestroy() {
        mediaSession?.run {
            player.release()
            release()
            mediaSession = null
        }
        super.onDestroy()
    }
}

Ява

public class PlaybackService extends MediaSessionService {
    private MediaSession mediaSession = null;

    @Override
    public void onCreate() {
        super.onCreate();
        ExoPlayer player = new ExoPlayer.Builder(this).build();
        mediaSession = new MediaSession.Builder(this, player).build();
    }

    @Override
    public void onDestroy() {
        mediaSession.getPlayer().release();
        mediaSession.release();
        mediaSession = null;
        super.onDestroy();
    }
}

В вашем манифесте ваш класс Service с фильтром намерений MediaSessionService и запросит разрешение FOREGROUND_SERVICE для запуска службы переднего плана:

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaSessionService"/>
    </intent-filter>
</service>

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

Наконец, в созданном вами классе переопределите метод onGetSession() , чтобы контролировать доступ клиента к вашему медиа-сеансу. Верните MediaSession , чтобы принять запрос на соединение, или верните null , чтобы отклонить запрос.

Котлин

// This example always accepts the connection request
override fun onGetSession(
    controllerInfo: MediaSession.ControllerInfo
): MediaSession? = mediaSession

Ява

@Override
public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) {
  // This example always accepts the connection request
  return mediaSession;
}

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

Теперь, когда ваш медиа-сеанс находится в Service отдельном от Activity или Fragment , в котором находится пользовательский интерфейс вашего проигрывателя, вы можете использовать MediaController , чтобы связать их вместе. В методе onStart() Activity или Fragment вашего пользовательского интерфейса создайте SessionToken для вашего MediaSession , затем используйте SessionToken для создания MediaController . Создание MediaController происходит асинхронно.

Котлин

override fun onStart() {
  val sessionToken = SessionToken(this, ComponentName(this, PlaybackService::class.java))
  val controllerFuture = MediaController.Builder(this, sessionToken).buildAsync()
  controllerFuture.addListener(
    {
        // Call controllerFuture.get() to retrieve the MediaController.
        // MediaController implements the Player interface, so it can be
        // attached to the PlayerView UI component.
        playerView.setPlayer(controllerFuture.get())
      },
    MoreExecutors.directExecutor()
  )
}

Ява

@Override
public void onStart() {
  SessionToken sessionToken =
    new SessionToken(this, new ComponentName(this, PlaybackService.class));
  ListenableFuture<MediaController> controllerFuture =
    new MediaController.Builder(this, sessionToken).buildAsync();
  controllerFuture.addListener(() -> {
    // Call controllerFuture.get() to retrieve the MediaController.
    // MediaController implements the Player interface, so it can be
    // attached to the PlayerView UI component.
    playerView.setPlayer(controllerFuture.get());
  }, MoreExecutors.directExecutor())
}

MediaController реализует интерфейс Player , поэтому для управления воспроизведением можно использовать те же методы, что и play() и pause() . Как и в случае с другими компонентами, не забудьте освободить MediaController , когда он больше не нужен, например, метод жизненного цикла onStop() Activity , вызвав MediaController.releaseFuture() .

Публикация уведомления

Службы переднего плана должны публиковать уведомления, пока они активны. MediaSessionService автоматически создаст для вас уведомление MediaStyle в форме MediaNotification . Чтобы предоставить настраиваемое уведомление, создайте MediaNotification.Provider с помощью DefaultMediaNotificationProvider.Builder или создайте собственную реализацию интерфейса поставщика. Добавьте своего провайдера в свой MediaSession с помощью setMediaNotificationProvider .

Реклама вашей библиотеки контента

MediaLibraryService основан на MediaSessionService , позволяя клиентским приложениям просматривать мультимедийный контент, предоставляемый вашим приложением. Клиентские приложения реализуют MediaBrowser для взаимодействия с вашим MediaLibraryService .

Реализация MediaLibraryService аналогична реализации MediaSessionService , за исключением того, что в onGetSession() вы должны возвращать MediaLibrarySession вместо MediaSession . По сравнению с MediaSession.Callback MediaLibrarySession.Callback включает дополнительные методы, которые позволяют клиенту браузера перемещаться по содержимому, предлагаемому вашей библиотечной службой.

Подобно MediaSessionService , объявите MediaLibraryService в своем манифесте и запросите разрешение FOREGROUND_SERVICE для запуска службы переднего плана:

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaLibraryService"/>
        <action android:name="android.media.browse.MediaBrowserService"/>
    </intent-filter>
</service>

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

Приведенный выше пример включает фильтр намерений как для MediaLibraryService , так и для обратной совместимости для устаревшего MediaBrowserService . Дополнительный фильтр намерений позволяет клиентским приложениям, использующим API MediaBrowserCompat , распознавать вашу Service .

MediaLibrarySession позволяет обслуживать вашу библиотеку контента в древовидной структуре с одним корневым MediaItem . Каждый MediaItem в дереве может иметь любое количество дочерних узлов MediaItem . Вы можете использовать другой корень или другое дерево в зависимости от запроса клиентского приложения. Например, дерево, которое вы возвращаете клиенту в поисках списка рекомендуемых медиа-элементов, может содержать только корневой MediaItem и один уровень дочерних узлов MediaItem , тогда как дерево, которое вы возвращаете в другое клиентское приложение, может представлять собой более полную библиотеку содержание.

Создание MediaLibrarySession

MediaLibrarySession расширяет API MediaSession , добавляя API просмотра контента. По сравнению с обратным вызовом MediaSession , обратный вызов MediaLibrarySession добавляет такие методы, как:

  • onGetLibraryRoot() , когда клиент запрашивает корневой MediaItem дерева контента.
  • onGetChildren() когда клиент запрашивает дочерние элементы MediaItem в дереве контента
  • onGetSearchResult() когда клиент запрашивает результаты поиска из дерева контента для данного запроса

Соответствующие методы обратного вызова будут включать объект LibraryParams с дополнительными сигналами о типе дерева контента, который интересует клиентское приложение.

,

Jetpack Media3 определяет интерфейс Player , в котором описываются основные функции воспроизведения видео и аудио файлов. ExoPlayer является реализацией этого интерфейса по умолчанию в Media3. Мы рекомендуем использовать ExoPlayer, поскольку он предоставляет полный набор функций, которые охватывают большинство вариантов использования воспроизведения, и его можно настроить для обработки любых дополнительных вариантов использования, которые могут у вас возникнуть. ExoPlayer также абстрагирует фрагментацию устройств и ОС, поэтому ваш код работает согласованно во всей экосистеме Android. ExoPlayer включает в себя:

На этой странице описаны некоторые ключевые этапы создания приложения для воспроизведения, а для получения более подробной информации вы можете обратиться к нашим полным руководствам по Media3 ExoPlayer .

Начиная

Для начала добавьте зависимость от модулей ExoPlayer, UI и Common Jetpack Media3:

implementation "androidx.media3:media3-exoplayer:1.4.1"
implementation "androidx.media3:media3-ui:1.4.1"
implementation "androidx.media3:media3-common:1.4.1"

В зависимости от вашего варианта использования вам также могут понадобиться дополнительные модули из Media3, такие как exoplayer-dash для воспроизведения потоков в формате DASH.

Обязательно замените 1.4.1 предпочитаемой версией библиотеки. Вы можете обратиться к примечаниям к выпуску , чтобы увидеть последнюю версию.

Создание медиаплеера

С Media3 вы можете использовать встроенную реализацию интерфейса Player ExoPlayer или создать свою собственную реализацию.

Создание ExoPlayer

Самый простой способ создать экземпляр ExoPlayer заключается в следующем:

Котлин

val player = ExoPlayer.Builder(context).build()

Ява

ExoPlayer player = new ExoPlayer.Builder(context).build();

Вы можете создать свой медиаплеер в методе жизненного цикла onCreate() Activity , Fragment или Service , где он находится.

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

Media3 предоставляет компонент пользовательского интерфейса PlayerView , который вы можете включить в файл макета вашего приложения. Этот компонент инкапсулирует PlayerControlView для элементов управления воспроизведением, SubtitleView для отображения субтитров и Surface для рендеринга видео.

Подготовка игрока

Добавьте медиа-элементы в список воспроизведения для воспроизведения с помощью таких методов, как setMediaItem() и addMediaItem() . Затем вызовите prepare() , чтобы начать загрузку носителя и получить необходимые ресурсы.

Не следует выполнять эти действия, пока приложение не окажется на переднем плане. Если ваш проигрыватель находится в Activity или Fragment , это означает подготовку проигрывателя с помощью метода жизненного цикла onStart() на уровне API 24 и выше или метода жизненного цикла onResume() на уровне API 23 и ниже. Для игрока, находящегося в Service , вы можете подготовить его в onCreate() .

Управляйте игроком

После подготовки проигрывателя вы можете управлять воспроизведением, вызывая такие методы проигрывателя, как:

Компоненты пользовательского интерфейса, такие как PlayerView или PlayerControlView будут обновляться соответствующим образом при привязке к игроку.

Отпустить плеер

Для воспроизведения могут потребоваться ресурсы, количество которых ограничено, например видеодекодеры, поэтому важно вызывать release() на проигрывателе, чтобы освободить ресурсы, когда проигрыватель больше не нужен.

Если ваш проигрыватель находится в Activity или Fragment , освободите его с помощью метода жизненного цикла onStop() на уровне API 24 и выше или метода onPause() на уровне API 23 и ниже. Для игрока, находящегося в Service , вы можете освободить его с помощью onDestroy() .

Управление воспроизведением с помощью медиасеанса

В Android медиасеансы предоставляют стандартизированный способ взаимодействия с медиаплеером, невзирая на границы процесса. Подключение медиа-сеанса к вашему проигрывателю позволяет вам рекламировать воспроизведение мультимедиа извне и получать команды воспроизведения из внешних источников, например, для интеграции с системными элементами управления мультимедиа на мобильных устройствах и устройствах с большим экраном.

Чтобы использовать медиа-сессии, добавьте зависимость к модулю Media3 Session:

implementation "androidx.media3:media3-session:1.4.1"

Создать медиа-сессию

Вы можете создать MediaSession после инициализации проигрывателя следующим образом:

Котлин

val player = ExoPlayer.Builder(context).build()
val mediaSession = MediaSession.Builder(context, player).build()

Ява

ExoPlayer player = new ExoPlayer.Builder(context).build();
MediaSession mediaSession = new MediaSession.Builder(context, player).build();

Media3 автоматически синхронизирует состояние Player с состоянием MediaSession . Это работает с любой реализацией Player , включая ExoPlayer , CastPlayer или пользовательскую реализацию.

Предоставить контроль другим клиентам

Клиентские приложения могут реализовать медиа-контроллер для управления воспроизведением вашего медиа-сеанса. Чтобы получать эти запросы, установите объект обратного вызова при создании MediaSession .

Когда контроллер собирается подключиться к вашему медиа-сеансу, вызывается метод onConnect() . Вы можете использовать предоставленную ControllerInfo чтобы решить, принять или отклонить запрос. См. пример этого в демонстрационном приложении Media3 Session .

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

Другие методы обратного вызова позволяют обрабатывать, например, запросы на пользовательские команды воспроизведения и изменение списка воспроизведения . Эти обратные вызовы также включают объект ControllerInfo , поэтому вы можете определять управление доступом для каждого запроса.

Воспроизведение мультимедиа в фоновом режиме

Чтобы продолжить воспроизведение мультимедиа, когда ваше приложение не находится на переднем плане, например для воспроизведения музыки, аудиокниг или подкастов, даже если у пользователя не открыто ваше приложение, ваш Player и MediaSession должны быть инкапсулированы в службу переднего плана . Для этой цели Media3 предоставляет интерфейс MediaSessionService .

Реализация MediaSessionService

Создайте класс, расширяющий MediaSessionService , и создайте экземпляр MediaSession в методе жизненного цикла onCreate() .

Котлин

class PlaybackService : MediaSessionService() {
    private var mediaSession: MediaSession? = null

    // Create your Player and MediaSession in the onCreate lifecycle event
    override fun onCreate() {
        super.onCreate()
        val player = ExoPlayer.Builder(this).build()
        mediaSession = MediaSession.Builder(this, player).build()
    }

    // Remember to release the player and media session in onDestroy
    override fun onDestroy() {
        mediaSession?.run {
            player.release()
            release()
            mediaSession = null
        }
        super.onDestroy()
    }
}

Ява

public class PlaybackService extends MediaSessionService {
    private MediaSession mediaSession = null;

    @Override
    public void onCreate() {
        super.onCreate();
        ExoPlayer player = new ExoPlayer.Builder(this).build();
        mediaSession = new MediaSession.Builder(this, player).build();
    }

    @Override
    public void onDestroy() {
        mediaSession.getPlayer().release();
        mediaSession.release();
        mediaSession = null;
        super.onDestroy();
    }
}

В вашем манифесте ваш класс Service с фильтром намерений MediaSessionService и запросит разрешение FOREGROUND_SERVICE для запуска службы переднего плана:

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaSessionService"/>
    </intent-filter>
</service>

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

Наконец, в созданном вами классе переопределите метод onGetSession() , чтобы контролировать доступ клиента к вашему медиа-сеансу. Верните MediaSession , чтобы принять запрос на соединение, или верните null , чтобы отклонить запрос.

Котлин

// This example always accepts the connection request
override fun onGetSession(
    controllerInfo: MediaSession.ControllerInfo
): MediaSession? = mediaSession

Ява

@Override
public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) {
  // This example always accepts the connection request
  return mediaSession;
}

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

Теперь, когда ваш медиа-сеанс находится в Service отдельном от Activity или Fragment , в котором находится пользовательский интерфейс вашего проигрывателя, вы можете использовать MediaController , чтобы связать их вместе. В методе onStart() Activity или Fragment вашего пользовательского интерфейса создайте SessionToken для вашего MediaSession , затем используйте SessionToken для создания MediaController . Создание MediaController происходит асинхронно.

Котлин

override fun onStart() {
  val sessionToken = SessionToken(this, ComponentName(this, PlaybackService::class.java))
  val controllerFuture = MediaController.Builder(this, sessionToken).buildAsync()
  controllerFuture.addListener(
    {
        // Call controllerFuture.get() to retrieve the MediaController.
        // MediaController implements the Player interface, so it can be
        // attached to the PlayerView UI component.
        playerView.setPlayer(controllerFuture.get())
      },
    MoreExecutors.directExecutor()
  )
}

Ява

@Override
public void onStart() {
  SessionToken sessionToken =
    new SessionToken(this, new ComponentName(this, PlaybackService.class));
  ListenableFuture<MediaController> controllerFuture =
    new MediaController.Builder(this, sessionToken).buildAsync();
  controllerFuture.addListener(() -> {
    // Call controllerFuture.get() to retrieve the MediaController.
    // MediaController implements the Player interface, so it can be
    // attached to the PlayerView UI component.
    playerView.setPlayer(controllerFuture.get());
  }, MoreExecutors.directExecutor())
}

MediaController реализует интерфейс Player , поэтому для управления воспроизведением можно использовать те же методы, что и play() и pause() . Как и в случае с другими компонентами, не забудьте освободить MediaController , когда он больше не нужен, например, метод жизненного цикла onStop() Activity , вызвав MediaController.releaseFuture() .

Публикация уведомления

Службы переднего плана должны публиковать уведомления, пока они активны. MediaSessionService автоматически создаст для вас уведомление MediaStyle в форме MediaNotification . Чтобы предоставить настраиваемое уведомление, создайте MediaNotification.Provider с помощью DefaultMediaNotificationProvider.Builder или создайте собственную реализацию интерфейса поставщика. Добавьте своего провайдера в свой MediaSession с помощью setMediaNotificationProvider .

Реклама вашей библиотеки контента

MediaLibraryService основан на MediaSessionService , позволяя клиентским приложениям просматривать мультимедийный контент, предоставляемый вашим приложением. Клиентские приложения реализуют MediaBrowser для взаимодействия с вашим MediaLibraryService .

Реализация MediaLibraryService аналогична реализации MediaSessionService , за исключением того, что в onGetSession() вы должны возвращать MediaLibrarySession вместо MediaSession . По сравнению с MediaSession.Callback MediaLibrarySession.Callback включает дополнительные методы, которые позволяют клиенту браузера перемещаться по содержимому, предлагаемому вашей библиотечной службой.

Подобно MediaSessionService , объявите MediaLibraryService в своем манифесте и запросите разрешение FOREGROUND_SERVICE для запуска службы переднего плана:

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaLibraryService"/>
        <action android:name="android.media.browse.MediaBrowserService"/>
    </intent-filter>
</service>

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

Приведенный выше пример включает фильтр намерений как для MediaLibraryService , так и для обратной совместимости для устаревшего MediaBrowserService . Дополнительный фильтр намерений позволяет клиентским приложениям, использующим API MediaBrowserCompat , распознавать вашу Service .

MediaLibrarySession позволяет обслуживать вашу библиотеку контента в древовидной структуре с одним корневым MediaItem . Каждый MediaItem в дереве может иметь любое количество дочерних узлов MediaItem . Вы можете использовать другой корень или другое дерево в зависимости от запроса клиентского приложения. Например, дерево, которое вы возвращаете клиенту в поисках списка рекомендуемых медиа-элементов, может содержать только корневой MediaItem и один уровень дочерних узлов MediaItem , тогда как дерево, которое вы возвращаете в другое клиентское приложение, может представлять собой более полную библиотеку содержание.

Создание MediaLibrarySession

MediaLibrarySession расширяет API MediaSession , добавляя API просмотра контента. По сравнению с обратным вызовом MediaSession , обратный вызов MediaLibrarySession добавляет такие методы, как:

  • onGetLibraryRoot() , когда клиент запрашивает корневой MediaItem дерева контента.
  • onGetChildren() когда клиент запрашивает дочерние элементы MediaItem в дереве контента
  • onGetSearchResult() когда клиент запрашивает результаты поиска из дерева контента для данного запроса

Соответствующие методы обратного вызова будут включать объект LibraryParams с дополнительными сигналами о типе дерева контента, который интересует клиентское приложение.

,

Jetpack Media3 определяет интерфейс Player , в котором описываются основные функции воспроизведения видео и аудио файлов. ExoPlayer является реализацией этого интерфейса по умолчанию в Media3. Мы рекомендуем использовать ExoPlayer, поскольку он предоставляет полный набор функций, которые охватывают большинство вариантов использования воспроизведения, и его можно настроить для любых дополнительных вариантов использования, которые могут у вас возникнуть. ExoPlayer также абстрагирует фрагментацию устройств и ОС, поэтому ваш код работает согласованно во всей экосистеме Android. ExoPlayer включает в себя:

На этой странице описаны некоторые ключевые этапы создания приложения для воспроизведения, а для получения более подробной информации вы можете обратиться к нашим полным руководствам по Media3 ExoPlayer .

Начиная

Для начала добавьте зависимость от модулей ExoPlayer, UI и Common Jetpack Media3:

implementation "androidx.media3:media3-exoplayer:1.4.1"
implementation "androidx.media3:media3-ui:1.4.1"
implementation "androidx.media3:media3-common:1.4.1"

В зависимости от вашего варианта использования вам также могут понадобиться дополнительные модули из Media3, такие как exoplayer-dash для воспроизведения потоков в формате DASH.

Обязательно замените 1.4.1 предпочитаемой версией библиотеки. Вы можете обратиться к примечаниям к выпуску , чтобы увидеть последнюю версию.

Создание медиаплеера

С Media3 вы можете использовать встроенную реализацию интерфейса Player ExoPlayer или создать свою собственную реализацию.

Создание ExoPlayer

Самый простой способ создать экземпляр ExoPlayer заключается в следующем:

Котлин

val player = ExoPlayer.Builder(context).build()

Ява

ExoPlayer player = new ExoPlayer.Builder(context).build();

Вы можете создать свой медиаплеер в методе жизненного цикла onCreate() Activity , Fragment или Service , где он находится.

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

Media3 предоставляет компонент пользовательского интерфейса PlayerView , который вы можете включить в файл макета вашего приложения. Этот компонент инкапсулирует PlayerControlView для элементов управления воспроизведением, SubtitleView для отображения субтитров и Surface для рендеринга видео.

Подготовка игрока

Добавьте медиа-элементы в список воспроизведения для воспроизведения с помощью таких методов, как setMediaItem() и addMediaItem() . Затем вызовите prepare() , чтобы начать загрузку носителя и получить необходимые ресурсы.

Не следует выполнять эти действия, пока приложение не окажется на переднем плане. Если ваш проигрыватель находится в Activity или Fragment , это означает подготовку проигрывателя с помощью метода жизненного цикла onStart() на уровне API 24 и выше или метода жизненного цикла onResume() на уровне API 23 и ниже. Для игрока, находящегося в Service , вы можете подготовить его в onCreate() .

Управляйте игроком

После подготовки проигрывателя вы можете управлять воспроизведением, вызывая такие методы проигрывателя, как:

Компоненты пользовательского интерфейса, такие как PlayerView или PlayerControlView будут обновляться соответствующим образом при привязке к игроку.

Отпустить плеер

Для воспроизведения могут потребоваться ресурсы, количество которых ограничено, например видеодекодеры, поэтому важно вызывать release() на проигрывателе, чтобы освободить ресурсы, когда проигрыватель больше не нужен.

Если ваш проигрыватель находится в Activity или Fragment , освободите его с помощью метода жизненного цикла onStop() на уровне API 24 и выше или метода onPause() на уровне API 23 и ниже. Для игрока, находящегося в Service , вы можете освободить его с помощью onDestroy() .

Управление воспроизведением с помощью медиасеанса

В Android медиасеансы предоставляют стандартизированный способ взаимодействия с медиаплеером, невзирая на границы процесса. Подключение медиа-сеанса к вашему проигрывателю позволяет вам рекламировать воспроизведение мультимедиа извне и получать команды воспроизведения из внешних источников, например, для интеграции с системными элементами управления мультимедиа на мобильных устройствах и устройствах с большим экраном.

Чтобы использовать медиа-сессии, добавьте зависимость к модулю Media3 Session:

implementation "androidx.media3:media3-session:1.4.1"

Создать медиа-сессию

Вы можете создать MediaSession после инициализации проигрывателя следующим образом:

Котлин

val player = ExoPlayer.Builder(context).build()
val mediaSession = MediaSession.Builder(context, player).build()

Ява

ExoPlayer player = new ExoPlayer.Builder(context).build();
MediaSession mediaSession = new MediaSession.Builder(context, player).build();

Media3 автоматически синхронизирует состояние Player с состоянием MediaSession . Это работает с любой реализацией Player , включая ExoPlayer , CastPlayer или пользовательскую реализацию.

Предоставить контроль другим клиентам

Клиентские приложения могут реализовать медиа-контроллер для управления воспроизведением вашего медиа-сеанса. Чтобы получать эти запросы, установите объект обратного вызова при создании MediaSession .

Когда контроллер собирается подключиться к вашему медиа-сеансу, вызывается метод onConnect() . Вы можете использовать предоставленную ControllerInfo чтобы решить, принять или отклонить запрос. См. пример этого в демонстрационном приложении Media3 Session .

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

Другие методы обратного вызова позволяют обрабатывать, например, запросы на пользовательские команды воспроизведения и изменение списка воспроизведения . Эти обратные вызовы также включают объект ControllerInfo , поэтому вы можете определять управление доступом для каждого запроса.

Воспроизведение мультимедиа в фоновом режиме

Чтобы продолжить воспроизведение мультимедиа, когда ваше приложение не находится на переднем плане, например для воспроизведения музыки, аудиокниг или подкастов, даже если у пользователя не открыто ваше приложение, ваш Player и MediaSession должны быть инкапсулированы в службу переднего плана . Для этой цели Media3 предоставляет интерфейс MediaSessionService .

Реализация MediaSessionService

Создайте класс, расширяющий MediaSessionService , и создайте экземпляр MediaSession в методе жизненного цикла onCreate() .

Котлин

class PlaybackService : MediaSessionService() {
    private var mediaSession: MediaSession? = null

    // Create your Player and MediaSession in the onCreate lifecycle event
    override fun onCreate() {
        super.onCreate()
        val player = ExoPlayer.Builder(this).build()
        mediaSession = MediaSession.Builder(this, player).build()
    }

    // Remember to release the player and media session in onDestroy
    override fun onDestroy() {
        mediaSession?.run {
            player.release()
            release()
            mediaSession = null
        }
        super.onDestroy()
    }
}

Ява

public class PlaybackService extends MediaSessionService {
    private MediaSession mediaSession = null;

    @Override
    public void onCreate() {
        super.onCreate();
        ExoPlayer player = new ExoPlayer.Builder(this).build();
        mediaSession = new MediaSession.Builder(this, player).build();
    }

    @Override
    public void onDestroy() {
        mediaSession.getPlayer().release();
        mediaSession.release();
        mediaSession = null;
        super.onDestroy();
    }
}

В вашем манифесте ваш класс Service с фильтром намерений MediaSessionService и запросит разрешение FOREGROUND_SERVICE для запуска службы переднего плана:

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaSessionService"/>
    </intent-filter>
</service>

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

Наконец, в созданном вами классе переопределите метод onGetSession() , чтобы контролировать доступ клиента к вашему медиа-сеансу. Верните MediaSession , чтобы принять запрос на соединение, или верните null , чтобы отклонить запрос.

Котлин

// This example always accepts the connection request
override fun onGetSession(
    controllerInfo: MediaSession.ControllerInfo
): MediaSession? = mediaSession

Ява

@Override
public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) {
  // This example always accepts the connection request
  return mediaSession;
}

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

Теперь, когда ваш медиа-сеанс находится в Service отдельном от Activity или Fragment , в котором находится пользовательский интерфейс вашего проигрывателя, вы можете использовать MediaController , чтобы связать их вместе. В методе onStart() Activity или Fragment вашего пользовательского интерфейса создайте SessionToken для вашего MediaSession , затем используйте SessionToken для создания MediaController . Создание MediaController происходит асинхронно.

Котлин

override fun onStart() {
  val sessionToken = SessionToken(this, ComponentName(this, PlaybackService::class.java))
  val controllerFuture = MediaController.Builder(this, sessionToken).buildAsync()
  controllerFuture.addListener(
    {
        // Call controllerFuture.get() to retrieve the MediaController.
        // MediaController implements the Player interface, so it can be
        // attached to the PlayerView UI component.
        playerView.setPlayer(controllerFuture.get())
      },
    MoreExecutors.directExecutor()
  )
}

Ява

@Override
public void onStart() {
  SessionToken sessionToken =
    new SessionToken(this, new ComponentName(this, PlaybackService.class));
  ListenableFuture<MediaController> controllerFuture =
    new MediaController.Builder(this, sessionToken).buildAsync();
  controllerFuture.addListener(() -> {
    // Call controllerFuture.get() to retrieve the MediaController.
    // MediaController implements the Player interface, so it can be
    // attached to the PlayerView UI component.
    playerView.setPlayer(controllerFuture.get());
  }, MoreExecutors.directExecutor())
}

MediaController реализует интерфейс Player , поэтому для управления воспроизведением можно использовать те же методы, что и play() и pause() . Как и в случае с другими компонентами, не забудьте освободить MediaController , когда он больше не нужен, например, метод жизненного цикла onStop() Activity , вызвав MediaController.releaseFuture() .

Публикация уведомления

Службы переднего плана должны публиковать уведомления, пока они активны. MediaSessionService автоматически создаст для вас уведомление MediaStyle в форме MediaNotification . Чтобы предоставить настраиваемое уведомление, создайте MediaNotification.Provider с помощью DefaultMediaNotificationProvider.Builder или создайте собственную реализацию интерфейса поставщика. Добавьте своего провайдера в свой MediaSession с помощью setMediaNotificationProvider .

Реклама вашей библиотеки контента

MediaLibraryService основан на MediaSessionService , позволяя клиентским приложениям просматривать мультимедийный контент, предоставляемый вашим приложением. Клиентские приложения реализуют MediaBrowser для взаимодействия с вашим MediaLibraryService .

Реализация MediaLibraryService аналогична реализации MediaSessionService , за исключением того, что в onGetSession() вы должны возвращать MediaLibrarySession вместо MediaSession . По сравнению с MediaSession.Callback MediaLibrarySession.Callback включает дополнительные методы, которые позволяют клиенту браузера перемещаться по содержимому, предлагаемому вашей библиотечной службой.

Подобно MediaSessionService , объявите MediaLibraryService в своем манифесте и запросите разрешение FOREGROUND_SERVICE для запуска службы переднего плана:

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaLibraryService"/>
        <action android:name="android.media.browse.MediaBrowserService"/>
    </intent-filter>
</service>

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

Приведенный выше пример включает фильтр намерений как для MediaLibraryService , так и для обратной совместимости для устаревшего MediaBrowserService . Дополнительный фильтр намерений позволяет клиентским приложениям, использующим API MediaBrowserCompat , распознавать вашу Service .

MediaLibrarySession позволяет обслуживать вашу библиотеку контента в древовидной структуре с одним корневым MediaItem . Каждый MediaItem в дереве может иметь любое количество дочерних узлов MediaItem . Вы можете использовать другой корень или другое дерево в зависимости от запроса клиентского приложения. Например, дерево, которое вы возвращаете клиенту в поисках списка рекомендуемых медиа-элементов, может содержать только корневой MediaItem и один уровень дочерних узлов MediaItem , тогда как дерево, которое вы возвращаете в другое клиентское приложение, может представлять собой более полную библиотеку содержание.

Создание MediaLibrarySession

MediaLibrarySession расширяет API MediaSession , добавляя API просмотра контента. По сравнению с обратным вызовом MediaSession , обратный вызов MediaLibrarySession добавляет такие методы, как:

  • onGetLibraryRoot() , когда клиент запрашивает корневой MediaItem дерева контента.
  • onGetChildren() когда клиент запрашивает дочерние элементы MediaItem в дереве контента
  • onGetSearchResult() когда клиент запрашивает результаты поиска из дерева контента для данного запроса

Соответствующие методы обратного вызова будут включать объект LibraryParams с дополнительными сигналами о типе дерева контента, который интересует клиентское приложение.