Руководство по миграции на 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 и плагин Android Studio Gradle до последней версии, которая работает с обновленными зависимостями сверху. Например:

      • Версия плагина Android Gradle: 7.1.0
      • Версия Gradle: 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 , затем Собрать > Очистить проект , а затем Собрать > Пересобрать проект (отслеживайте сборку на вкладке «Сборка - Выходные данные сборки» в Android Studio) .

Рекомендуемые последующие шаги:

  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.7.1"
    
  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 .

MediaSessionConnector AndroidX Media3
CustomActionProvider MediaSession.Callback.onCustomCommand()/ MediaSession.setMediaButtonPreferences()
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.7.1"
    
  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 вы можете увидеть ошибки lint о нестабильном использовании API. Эти API безопасны в использовании, а ошибки lint являются побочным продуктом наших новых гарантий двоичной совместимости. Если вам не требуется строгая двоичная совместимость, эти ошибки можно безопасно подавить с помощью аннотации @OptIn .

Фон

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

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

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

Улучшения в Media3

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

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

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

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

Устаревшие API

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

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

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

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