Руководство по миграции на AndroidX Media3

Приложения, которые в настоящее время используют автономную библиотеку com.google.android.exoplayer2 и androidx.media должны перейти на androidx.media3 . Используйте сценарий миграции для переноса файлов сборки Gradle, исходных файлов Java и Kotlin, а также файлов макета XML из ExoPlayer 2.19.1 в AndroidX Media3 1.1.1 .

Обзор

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

Зачем переходить на Jetpack Media3

  • Это новый дом ExoPlayer , тогда как com.google.android.exoplayer2 прекращена.
  • Получите доступ к API-интерфейсу проигрывателя через компоненты/процессы с помощью MediaBrowser / MediaController .
  • Используйте расширенные возможности API MediaSession и MediaController .
  • Рекламируйте возможности воспроизведения с помощью детального контроля доступа .
  • Упростите свое приложение , удалив MediaSessionConnector и PlayerNotificationManager .
  • Обратная совместимость с клиентскими API-интерфейсами медиа-совместимости ( MediaBrowserCompat / MediaControllerCompat / MediaMetadataCompat )

Медиа API для перехода на AndroidX Media3

  • ExoPlayer и его расширения
    Сюда входят все модули устаревшего проекта ExoPlayer, за исключением модуля mediasession , выпуск которого прекращен. Приложения или модули, зависящие от пакетов в com.google.android.exoplayer2 можно перенести с помощью сценария миграции.
  • MediaSessionConnector (в зависимости от пакетов androidx.media.* androidx.media:media:1.4.3+ )
    Удалите MediaSessionConnector и используйте вместо него androidx.media3.session.MediaSession .
  • MediaBrowserServiceCompat (в зависимости от пакетов androidx.media.* androidx.media:media:1.4.3+ )
    Перенесите подклассы androidx.media.MediaBrowserServiceCompat в androidx.media3.session.MediaLibraryService и кодируйте с помощью MediaBrowserCompat.MediaItem в androidx.media3.common.MediaItem .
  • MediaBrowserCompat (в зависимости от пакетов android.support.v4.media.* androidx.media:media:1.4.3+ )
    Перенесите клиентский код с помощью MediaBrowserCompat или MediaControllerCompat чтобы использовать androidx.media3.session.MediaBrowser с androidx.media3.common.MediaItem .

Предварительные условия

  1. Убедитесь, что ваш проект находится под контролем исходного кода

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

  2. Обновите свое приложение

    • Мы рекомендуем обновить ваш проект, чтобы использовать самую последнюю версию библиотеки ExoPlayer и удалить все вызовы устаревших методов. Если вы собираетесь использовать сценарий для миграции, вам необходимо сопоставить версию, которую вы обновляете, с версией, обрабатываемой сценарием.

    • Увеличьте значение compileSdkVersion вашего приложения как минимум до 32 .

    • Обновите Gradle и плагин Gradle для Android Studio до последней версии, которая работает с обновленными зависимостями, указанными выше. Например:

      • Версия плагина Android Gradle: 7.1.0
      • Версия Градла: 7.4
    • Замените все операторы импорта с подстановочными знаками , в которых используется звездочка (*), и используйте полные операторы импорта: удалите операторы импорта с подстановочными знаками и используйте Android Studio для импорта полных операторов (F2 — Alt/Enter, F2 — Alt/Enter, . ..).

    • Перейдите с com.google.android.exoplayer2.PlayerView на com.google.android.exoplayer2.StyledPlayerView . Это необходимо, поскольку в AndroidX Media3 нет эквивалента com.google.android.exoplayer2.PlayerView .

Миграция ExoPlayer с поддержкой сценариев

Скрипт облегчает переход от com.google.android.exoplayer2 к новой структуре пакета и модуля под androidx.media3 . Сценарий применяет некоторые проверки вашего проекта и печатает предупреждения в случае неудачной проверки. В противном случае он применяет сопоставления переименованных классов и пакетов в ресурсах проекта Android Gradle, написанного на Java или Kotlin.

usage: ./media3-migration.sh [-p|-c|-d|-v]|[-m|-l [-x <path>] [-f] PROJECT_ROOT]
 PROJECT_ROOT: path to your project root (location of 'gradlew')
 -p: list package mappings and then exit
 -c: list class mappings (precedence over package mappings) and then exit
 -d: list dependency mappings and then exit
 -l: list files that will be considered for rewrite and then exit
 -x: exclude the path from the list of file to be changed: 'app/src/test'
 -m: migrate packages, classes and dependencies to AndroidX Media3
 -f: force the action even when validation fails
 -v: print the exoplayer2/media3 version strings of this script
 -h, --help: show this help text

Использование сценария миграции

  1. Загрузите сценарий миграции из тега проекта ExoPlayer на GitHub, соответствующего версии, до которой вы обновили свое приложение:

    curl -o media3-migration.sh \
      "https://raw.githubusercontent.com/google/ExoPlayer/r2.19.1/media3-migration.sh"
    
  2. Сделайте скрипт исполняемым:

    chmod 744 media3-migration.sh
    
  3. Запустите сценарий с --help чтобы узнать о параметрах.

  4. Запустите сценарий с -l , чтобы просмотреть набор файлов, выбранных для миграции (используйте -f , чтобы вывести список без предупреждений):

    ./media3-migration.sh -l -f /path/to/gradle/project/root
    
  5. Запустите сценарий с -m , чтобы сопоставить пакеты, классы и модули с Media3. Запуск сценария с опцией -m применит изменения к выбранным файлам.

    • Остановиться при ошибке проверки без внесения изменений
    ./media3-migration.sh -m /path/to/gradle/project/root
    
    • Принудительное исполнение

    Если сценарий обнаружит нарушение предварительных условий, миграцию можно запустить принудительно с помощью флага -f :

    ./media3-migration.sh -m -f /path/to/gradle/project/root
    
 # list files selected for migration when excluding paths
 ./media3-migration.sh -l -x "app/src/test/" -x "service/" /path/to/project/root
 # migrate the selected files
 ./media3-migration.sh -m -x "app/src/test/" -x "service/" /path/to/project/root

Выполните эти действия вручную после запуска сценария с опцией -m :

  1. Проверьте, как сценарий изменил ваш код : используйте инструмент сравнения и исправьте потенциальные проблемы (подумайте о том, чтобы сообщить об ошибке, если вы считаете, что в сценарии есть общая проблема, которая возникла без передачи опции -f ).
  2. Создайте проект : либо используйте ./gradlew clean build , либо в Android Studio выберите «Файл» > «Синхронизировать проект с файлами Gradle» , затем «Создать» > «Очистить проект» , а затем «Создать» > «Перестроить проект» (отслеживайте свою сборку на вкладке «Сборка — вывод сборки»). Андроид студия .

Рекомендуемые последующие действия:

  1. Устраните ошибки, связанные с использованием нестабильных API .
  2. Заменить устаревшие вызовы API . Используйте предложенный API замены. Наведите указатель на предупреждение в Android Studio и обратитесь к JavaDoc устаревшего символа, чтобы узнать, что использовать вместо данного вызова.
  3. Сортировка операторов импорта . Откройте проект в Android Studio, затем щелкните правой кнопкой мыши узел папки пакета в средстве просмотра проекта и выберите «Оптимизировать импорт пакетов, содержащих измененные исходные файлы».

Замените MediaSessionConnector на androidx.media3.session.MediaSession

В устаревшем мире MediaSessionCompat MediaSessionConnector отвечал за синхронизацию состояния проигрывателя с состоянием сеанса и получение команд от контроллеров, которым требовалось делегирование соответствующим методам проигрывателя. В AndroidX Media3 это выполняется напрямую с помощью MediaSession без необходимости подключения.

  1. Удалите все ссылки и использование MediaSessionConnector. Если вы использовали автоматизированный сценарий для миграции классов и пакетов ExoPlayer, то сценарий, скорее всего, оставил ваш код в некомпилируемом состоянии относительно MediaSessionConnector , которое невозможно разрешить. Android Studio покажет вам неработающий код, когда вы попытаетесь собрать или запустить приложение.

  2. В файле build.gradle , где вы сохраняете свои зависимости, добавьте зависимость реализации к модулю сеанса AndroidX Media3 и удалите устаревшую зависимость:

    implementation "androidx.media3:media3-session:1.5.0"
    
  3. Замените MediaSessionCompat на androidx.media3.session.MediaSession .

  4. На сайте кода, где вы создали устаревший MediaSessionCompat , используйте androidx.media3.session.MediaSession.Builder для создания MediaSession . Передайте игроку, чтобы построить построитель сеансов.

    val player = ExoPlayer.Builder(context).build()
    mediaSession = MediaSession.Builder(context, player)
        .setSessionCallback(MySessionCallback())
        .build()
    
  5. Реализуйте MySessionCallback в соответствии с требованиями вашего приложения. Это необязательно. Если вы хотите разрешить контроллерам добавлять элементы мультимедиа в проигрыватель, реализуйте MediaSession.Callback.onAddMediaItems() . Он обслуживает различные текущие и устаревшие методы API, которые добавляют элементы мультимедиа в проигрыватель для воспроизведения с обратной совместимостью. Сюда входят методы MediaController.set/addMediaItems() контроллера Media3, а также методы TransportControls.prepareFrom*/playFrom* устаревшего API. Пример реализации onAddMediaItems можно найти в PlaybackService демонстрационного приложения сеанса .

  6. Освободите сеанс мультимедиа на сайте кода, где вы уничтожили сеанс перед миграцией:

    mediaSession?.run {
      player.release()
      release()
      mediaSession = null
    }
    

Функциональность MediaSessionConnector в Media3

В следующей таблице показаны API-интерфейсы Media3, которые обрабатывают функции, ранее реализованные в MediaSessionConnector .

Медиасессионконнектор AndroidX Media3
CustomActionProvider MediaSession.Callback.onCustomCommand()/ MediaSession.setCustomLayout()
PlaybackPreparer MediaSession.Callback.onAddMediaItems() ( prepare() вызывается внутри)
QueueNavigator ForwardingSimpleBasePlayer
QueueEditor MediaSession.Callback.onAddMediaItems()
RatingCallback MediaSession.Callback.onSetRating()
PlayerNotificationManager DefaultMediaNotificationProvider/ MediaNotification.Provider

Миграция MediaBrowserService в MediaLibraryService

AndroidX Media3 представляет MediaLibraryService , который заменяет MediaBrowserServiceCompat . JavaDoc MediaLibraryService и его суперкласс MediaSessionService предоставляют хорошее введение в API и модель асинхронного программирования службы.

MediaLibraryService обратно совместим с MediaBrowserService . Клиентское приложение, использующее MediaBrowserCompat или MediaControllerCompat , продолжает работать без изменений кода при подключении к MediaLibraryService . Для клиента ясно, использует ли ваше приложение MediaLibraryService или устаревший MediaBrowserServiceCompat .

Диаграмма компонентов приложения со службой, активностью и внешними приложениями.
Рис. 1. Обзор компонента медиа-приложения.
  1. Чтобы обратная совместимость работала, вам необходимо зарегистрировать оба интерфейса службы с вашей службой в AndroidManifest.xml . Таким образом клиент находит ваш сервис по нужному сервисному интерфейсу:

    <service android:name=".MusicService" android:exported="true">
        <intent-filter>
            <action android:name="androidx.media3.session.MediaLibraryService"/>
            <action android:name="android.media.browse.MediaBrowserService" />
        </intent-filter>
    </service>
    
  2. В файле build.gradle , где вы сохраняете свои зависимости, добавьте зависимость реализации к модулю сеанса AndroidX Media3 и удалите устаревшую зависимость:

    implementation "androidx.media3:media3-session:1.5.0"
    
  3. Измените свой сервис, чтобы он наследовался от MediaLibraryService вместо MediaBrowserService . Как было сказано ранее, MediaLibraryService совместим с устаревшим MediaBrowserService . Соответственно, более широкий API, который сервис предлагает клиентам, остался прежним. Поэтому вполне вероятно, что приложение сможет сохранить большую часть логики, необходимой для реализации MediaBrowserService , и адаптировать ее для нового MediaLibraryService .

    Основные отличия от устаревшего MediaBrowserServiceCompat заключаются в следующем:

    • Реализуйте методы жизненного цикла службы. Методы, которые необходимо переопределить в самой службе, — это onCreate/onDestroy , где приложение выделяет/освобождает сеанс библиотеки, проигрыватель и другие ресурсы. В дополнение к стандартным методам жизненного цикла службы приложению необходимо переопределить onGetSession(MediaSession.ControllerInfo) , чтобы вернуть MediaLibrarySession , встроенный в onCreate .

    • Реализуйте MediaLibraryService.MediaLibrarySessionCallback: для создания сеанса требуется MediaLibraryService.MediaLibrarySessionCallback , который реализует фактические методы API домена. Таким образом, вместо переопределения методов API устаревшей службы вы переопределите методы MediaLibrarySession.Callback .

      Затем обратный вызов используется для создания MediaLibrarySession :

      mediaLibrarySession =
            MediaLibrarySession.Builder(this, player, MySessionCallback())
               .build()
      

      Полный API MediaLibrarySessionCallback можно найти в документации API.

    • Реализуйте MediaSession.Callback.onAddMediaItems() : обратный вызов onAddMediaItems(MediaSession, ControllerInfo, List<MediaItem>) обслуживает различные текущие и устаревшие методы API, которые добавляют элементы мультимедиа в проигрыватель для воспроизведения обратно совместимым способом. Сюда входят методы MediaController.set/addMediaItems() контроллера Media3, а также методы TransportControls.prepareFrom*/playFrom* устаревшего API. Пример реализации обратного вызова можно найти в PlaybackService демонстрационного приложения сеанса .

    • AndroidX Media3 использует androidx.media3.common.MediaItem вместо MediaBrowserCompat.MediaItem и MediaMetadataCompat . Части вашего кода, связанные с устаревшими классами, необходимо соответствующим образом изменить или вместо этого сопоставить с Media3 MediaItem .

    • Общая модель асинхронного программирования изменилась на Futures в отличие от подхода с отделяемым Result MediaBrowserServiceCompat . Реализация вашего сервиса может возвращать асинхронный ListenableFuture вместо отсоединения результата или возвращать немедленный Future для прямого возврата значения .

Удалить PlayerNotificationManager

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

Приложение может настроить уведомление , установив пользовательский MediaNotification.Provider в onCreate() , который заменяет DefaultMediaNotificationProvider . Затем MediaLibraryService по мере необходимости позаботится о запуске службы на переднем плане.

Переопределяя MediaLibraryService.updateNotification() приложение может дополнительно взять на себя полную ответственность за публикацию уведомления и запуск/остановку службы на переднем плане по мере необходимости.

Перенос клиентского кода с помощью MediaBrowser

В AndroidX Media3 MediaBrowser реализует интерфейсы MediaController/Player и может использоваться для управления воспроизведением мультимедиа, помимо просмотра медиатеки. Если вам нужно было создать MediaBrowserCompat и MediaControllerCompat в устаревшем мире, вы можете сделать то же самое, используя только MediaBrowser в Media3.

MediaBrowser можно создать и дождаться установления соединения с устанавливаемым сервисом:

scope.launch {
    val sessionToken =
        SessionToken(context, ComponentName(context, MusicService::class.java)
    browser =
        MediaBrowser.Builder(context, sessionToken))
            .setListener(BrowserListener())
            .buildAsync()
            .await()
    // Get the library root to start browsing the library.
    root = browser.getLibraryRoot(/* params= */ null).await();
    // Add a MediaController.Listener to listen to player state events.
    browser.addListener(playerListener)
    playerView.setPlayer(browser)
}

Ознакомьтесь с разделом Управление воспроизведением в сеансе мультимедиа, чтобы узнать, как создать MediaController для управления воспроизведением в фоновом режиме.

Дальнейшие действия и очистка

Нестабильные ошибки API

После перехода на Media3 вы можете увидеть ошибки, связанные с нестабильным использованием API. Эти API безопасны в использовании, а ошибки lint являются побочным продуктом наших новых гарантий двоичной совместимости. Если вам не требуется строгая двоичная совместимость, эти ошибки можно безопасно подавить с помощью аннотации @OptIn .

Фон

Ни ExoPlayer v1, ни v2 не давали строгих гарантий бинарной совместимости библиотеки между последующими версиями. Поверхность ExoPlayer API очень велика по своей конструкции, что позволяет приложениям настраивать практически каждый аспект воспроизведения. В последующих версиях ExoPlayer время от времени вносились переименования символов или другие критические изменения (например, новые обязательные методы в интерфейсах). В большинстве случаев эти поломки были устранены путем введения нового символа наряду с прекращением поддержки старого символа в нескольких версиях, чтобы дать разработчикам время на миграцию их использования, но это не всегда было возможно.

Эти критические изменения привели к двум проблемам для пользователей библиотек ExoPlayer v1 и v2:

  1. Обновление до версии ExoPlayer может привести к остановке компиляции кода.
  2. Приложение, которое зависело от ExoPlayer как напрямую, так и через промежуточную библиотеку, должно было гарантировать, что обе зависимости имеют одну и ту же версию, иначе двоичная несовместимость могла привести к сбоям во время выполнения.

Улучшения в Media3

Media3 гарантирует двоичную совместимость для части поверхности API. Части, которые не гарантируют двоичную совместимость, помечены @UnstableApi . Чтобы прояснить это различие, использование нестабильных символов API генерирует ошибку ворса, если они не помечены @OptIn .

После перехода с ExoPlayer v2 на Media3 вы можете увидеть множество нестабильных ошибок проверки API. Из-за этого может показаться, что Media3 «менее стабилен», чем ExoPlayer v2. Это не так. «Нестабильные» части API Media3 имеют тот же уровень стабильности, что и вся поверхность API ExoPlayer v2, а гарантии стабильной поверхности API Media3 вообще недоступны в ExoPlayer v2. Разница лишь в том, что ошибка ворса теперь предупреждает вас о различных уровнях стабильности.

Обработка нестабильных ошибок API

См . раздел устранения неполадок, посвященный этим ошибкам lint, для получения подробной информации о том, как аннотировать использование нестабильных API в Java и Kotlin с помощью @OptIn .

Устаревшие API

Вы можете заметить, что вызовы устаревших API в Android Studio перечеркнуты. Мы рекомендуем заменить такие вызовы соответствующей альтернативой. Наведите указатель мыши на символ, чтобы просмотреть документ JavaDoc, в котором указано, какой API использовать вместо этого.

Снимок экрана: как отобразить JavaDoc с альтернативой устаревшего метода
Рисунок 3. Подсказка JavaDoc в Android Studio предлагает альтернативу любому устаревшему символу.

Примеры кода и демонстрационные приложения

  • Демо-приложение сеанса AndroidX Media3 (мобильное устройство и WearOS)
    • Пользовательские действия
    • Уведомление системного интерфейса, MediaButton/BT
    • Google Ассистент управления воспроизведением
  • UAMP: Android Media Player (ветвь media3) (мобильный, AutomotiveOS)
    • Уведомление системного интерфейса, MediaButton/BT, возобновление воспроизведения
    • Управление воспроизведением Google Assistant/WearOS
    • AutomotiveOS: пользовательская команда и вход в систему