Руководство по миграции на 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 Player через компоненты/процессы осуществляется с помощью 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 , то в Media3 можно сделать то же самое, используя только MediaBrowser .

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

Фон

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

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

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

Улучшения в Media3

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

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

Обработка нестабильных ошибок 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: пользовательские команды и вход в систему