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 已停止使用。
  • 使用 MediaBrowser/MediaController 在组件/进程之间访问 **播放器 API**。
  • 使用 **MediaSessionMediaController 的扩展功能** API。
  • 使用 **细粒度的访问控制** 来宣传播放功能。
  • 通过移除 MediaSessionConnectorPlayerNotificationManager 来 **简化您的应用**。
  • 与媒体兼容客户端 API(MediaBrowserCompat/MediaControllerCompat/MediaMetadataCompat)**向后兼容**

要迁移到 AndroidX Media3 的媒体 API

  • ExoPlayer 及其扩展
    这包括旧版 ExoPlayer 项目 的所有模块,但已停止使用的 mediasession 模块除外。可以使用迁移脚本迁移依赖于 com.google.android.exoplayer2 中包的应用或模块。
  • MediaSessionConnector(依赖于 androidx.media:media:1.4.3+androidx.media.* 包)
    移除 MediaSessionConnector 并改用 androidx.media3.session.MediaSession
  • MediaBrowserServiceCompat(依赖于 androidx.media:media:1.4.3+androidx.media.* 包)
    androidx.media.MediaBrowserServiceCompat 的子类迁移到 androidx.media3.session.MediaLibraryService,并将使用 MediaBrowserCompat.MediaItem 的代码迁移到 androidx.media3.common.MediaItem
  • MediaBrowserCompat(依赖于 androidx.media:media:1.4.3+android.support.v4.media.* 包)
    将使用 MediaBrowserCompatMediaControllerCompat 的客户端代码迁移为使用带有 androidx.media3.common.MediaItemandroidx.media3.session.MediaBrowser

先决条件

  1. 确保您的项目在版本控制下

    确保您可以轻松地撤消脚本迁移工具应用的更改。如果您尚未将项目置于版本控制之下,现在是开始的好时机。如果由于某种原因您不想这样做,请在开始迁移之前备份您的项目。

  2. 更新您的应用

    • 我们建议您更新项目以使用 **ExoPlayer 库的最新版本** 并删除对已弃用方法的所有调用。如果您打算使用脚本进行迁移,则需要将您更新到的版本与脚本处理的版本匹配。

    • 将您的应用的 **compileSdkVersion 提高到至少 32**。

    • **升级 Gradle 和 Android Studio Gradle 插件**到与上述更新的依赖项兼容的最新版本。例如:

      • Android Gradle Plugin 版本: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 下的新包和模块结构。该脚本对您的项目应用一些验证检查,如果验证失败,则会打印警告。否则,它将应用重命名类和包的映射 到用 Java 或 Kotlin 编写的 Android Gradle 项目的资源中。

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. 从与您已更新应用的版本相对应的 GitHub 上的 ExoPlayer 项目的标签下载 迁移脚本

    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 运行脚本以列出为迁移选择的

    ./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.4.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 方法,这些方法以向后兼容的方式向播放器添加媒体项目以进行播放。这包括 Media3 控制器的MediaController.set/addMediaItems()方法,以及旧版 API 的TransportControls.prepareFrom*/playFrom*方法。onAddMediaItems 的示例实现可以在会话演示应用的PlaybackService中找到

  6. 在迁移之前销毁会话的代码位置释放媒体会话。

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

Media3 中的MediaSessionConnector 功能

下表显示了处理以前在MediaSessionConnector中实现的功能的 Media3 API。

MediaSessionConnectorAndroidX Media3
CustomActionProvider MediaSession.Callback.onCustomCommand()/ MediaSession.setCustomLayout()
PlaybackPreparer MediaSession.Callback.onAddMediaItems()(内部调用prepare()
QueueNavigator ForwardingPlayer
QueueEditor MediaSession.Callback.onAddMediaItems()
RatingCallback MediaSession.Callback.onSetRating()
PlayerNotificationManager DefaultMediaNotificationProvider/ MediaNotification.Provider

MediaBrowserService迁移到MediaLibraryService

AndroidX Media3 引入了MediaLibraryService,它取代了MediaBrowserServiceCompatMediaLibraryService及其超类MediaSessionService的 JavaDoc 提供了对 API 和服务异步编程模型的良好介绍。

MediaLibraryServiceMediaBrowserService向后兼容。当连接到MediaLibraryService时,使用MediaBrowserCompatMediaControllerCompat的客户端应用无需更改代码即可继续工作。对于客户端来说,您的应用是使用MediaLibraryService还是旧版MediaBrowserServiceCompat是透明的。

App component diagram with service, activity and external apps.
图 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.4.1"
    
  3. 将您的服务更改为继承自MediaLibraryService,而不是MediaBrowserService。如前所述,MediaLibraryService与旧版MediaBrowserService兼容。因此,服务向客户端提供的更广泛的 API 仍然相同。因此,应用很可能能够保留实现MediaBrowserService所需的大部分逻辑,并将其调整为新的MediaLibraryService

    与旧版MediaBrowserServiceCompat相比,主要区别如下:

    • 实现服务生命周期方法:需要在服务本身覆盖的方法是onCreate/onDestroy,应用在其中分配/释放库会话、播放器和其他资源。除了标准的服务生命周期方法外,应用还需要覆盖onGetSession(MediaSession.ControllerInfo)以返回在onCreate中构建的MediaLibrarySession

    • 实现 MediaLibraryService.MediaLibrarySessionCallback:构建会话需要一个MediaLibraryService.MediaLibrarySessionCallback,它实现实际的域 API 方法。因此,您将覆盖MediaLibrarySession.Callback的方法,而不是覆盖旧版服务API方法。

      然后使用回调来构建MediaLibrarySession

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

      在 API 文档中查找MediaLibrarySessionCallback 的完整 API

    • 实现MediaSession.Callback.onAddMediaItems():回调onAddMediaItems(MediaSession, ControllerInfo, List<MediaItem>)提供了各种当前和旧版 API 方法,这些方法以向后兼容的方式向播放器添加媒体项目以进行播放。这包括 Media3 控制器的MediaController.set/addMediaItems()方法,以及旧版 API 的TransportControls.prepareFrom*/playFrom*方法。可以在会话演示应用的PlaybackService中找到回调的示例实现

    • AndroidX Media3 使用androidx.media3.common.MediaItem代替MediaBrowserCompat.MediaItemMediaMetadataCompat。与旧版类相关的部分代码需要相应地更改或映射到 Media3 MediaItem

    • 一般的异步编程模型更改为Futures,与MediaBrowserServiceCompat的可分离Result方法相反。您的服务实现可以返回异步ListenableFuture,而不是分离结果或返回立即 Future 以直接返回值

删除 PlayerNotificationManager

MediaLibraryService自动支持媒体通知,使用MediaLibraryServiceMediaSessionService时,可以删除PlayerNotificationManager

应用可以通过在onCreate()中设置自定义MediaNotification.Provider自定义通知,该自定义MediaNotification.Provider将替换DefaultMediaNotificationProviderMediaLibraryService随后会处理按需启动前台服务。

通过覆盖MediaLibraryService.updateNotification(),应用可以进一步完全掌控发布通知以及按需启动/停止前台服务。

迁移使用 MediaBrowser 的客户端代码

在 AndroidX Media3 中,MediaBrowser实现了MediaController/Player接口,除了浏览媒体库之外,还可以用于控制媒体播放。如果您必须在旧版系统中创建MediaBrowserCompatMediaControllerCompat,则只需在 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 用法的 lint 错误。这些 API 可以安全使用,lint 错误是我们新的二进制兼容性保证的副产品。如果您不需要严格的二进制兼容性,则可以使用@OptIn注解安全地禁止这些错误。

背景

ExoPlayer v1 或 v2 都没有提供关于后续版本之间库的二进制兼容性的严格保证。ExoPlayer API 表面设计上非常大,以便允许应用自定义播放的几乎所有方面。ExoPlayer 的后续版本偶尔会引入符号重命名或其他重大更改(例如,接口上的新必需方法)。在大多数情况下,这些中断是通过在弃用旧符号的同时引入新符号几个版本来缓解的,以便为开发人员提供迁移其用法的时限,但这并非总是可能的。

这些重大更改导致 ExoPlayer v1 和 v2 库的用户出现两个问题:

  1. 升级到 ExoPlayer 版本可能会导致代码停止编译。
  2. 直接和通过中间库同时依赖 ExoPlayer 的应用必须确保两个依赖项的版本相同,否则二进制不兼容性可能会导致运行时崩溃。

Media3 的改进

Media3 保证 API 表面子集的二进制兼容性。保证二进制兼容性的部分用@UnstableApi标记。为了使这一区别更加清晰,除非使用@OptIn注解,否则使用不稳定 API 符号会生成 lint 错误。

从 ExoPlayer v2 迁移到 Media3 后,您可能会看到许多不稳定的 API lint 错误。这可能会让您觉得 Media3 比 ExoPlayer v2 “不稳定”。事实并非如此。Media3 API 的“不稳定”部分与 ExoPlayer v2 API 的**整体**稳定性级别相同,而 Media3 API 的稳定部分提供的保证在 ExoPlayer v2 中根本不存在。区别仅仅在于 lint 错误现在会提醒您不同级别的稳定性。

处理不稳定的 API lint 错误

有关如何使用@OptIn 注释 Java 和 Kotlin 中不稳定 API 用法的详细信息,请参阅有关这些 lint 错误的故障排除部分

已弃用的 API

您可能会注意到,在 Android Studio 中,对已弃用 API 的调用会被删除线标注。我们建议您使用相应的替代方法替换这些调用。将鼠标悬停在符号上,即可查看 JavaDoc,了解应改用哪个 API。

Screenshot: How to display JavaDoc with alternative of deprecated method
图 3:Android Studio 中的 JavaDoc 提示信息建议了任何已弃用符号的替代方法。

代码示例和演示应用