Jetpack Media3 定义了一个Player
接口,该接口概述了视频和音频文件播放的基本功能。ExoPlayer
是 Media3 中此接口的默认实现。我们建议使用 ExoPlayer,因为它提供了一套全面的功能,涵盖大多数播放用例,并且可以自定义以处理您可能遇到的任何其他用例。ExoPlayer 还抽象了设备和操作系统碎片,因此您的代码可在整个 Android 生态系统中一致运行。ExoPlayer 包括
- 对播放列表的支持
- 对各种渐进式和自适应流式格式的支持
- 对客户端和服务器端广告插入的支持
- 对受 DRM 保护的播放的支持
此页面将引导您完成构建播放应用的一些关键步骤,如需了解更多详情,您可以查看我们关于Media3 ExoPlayer的完整指南。
入门
要开始,请添加对 Jetpack Media3 的 ExoPlayer、UI 和 Common 模块的依赖项
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
实例的最简单方法如下所示
Kotlin
val player = ExoPlayer.Builder(context).build()
Java
ExoPlayer player = new ExoPlayer.Builder(context).build();
您可以在 Activity
、Fragment
或 Service
的 onCreate()
生命周期方法中创建媒体播放器。
Builder
包含一系列您可能感兴趣的自定义选项,例如
setAudioAttributes()
用于配置 音频焦点 处理setHandleAudioBecomingNoisy()
用于配置音频输出设备断开连接时的播放行为setTrackSelector()
用于配置 音轨选择
Media3 提供了一个 PlayerView
UI 组件,您可以将其包含在应用程序的布局文件中。此组件封装了一个用于播放控制的 PlayerControlView
,用于显示字幕的 SubtitleView
以及用于渲染视频的 Surface
。
准备播放器
使用诸如 setMediaItem()
和 addMediaItem()
等方法,将 媒体项目 添加到播放列表中。然后,调用 prepare()
开始加载媒体并获取必要的资源。
您不应该在应用程序处于前台之前执行这些步骤。如果您的播放器位于 Activity
或 Fragment
中,这意味着在 API 级别 24 及更高版本上在 onStart()
生命周期方法中准备播放器,在 API 级别 23 及以下版本上在 onResume()
生命周期方法中准备播放器。对于位于 Service
中的播放器,您可以在 onCreate()
中准备它。
控制播放器
播放器准备就绪后,您可以通过调用播放器上的方法来控制播放,例如
play()
和pause()
用于开始和暂停播放seekTo()
用于在当前媒体项目中跳转到某个位置seekToNextMediaItem()
和seekToPreviousMediaItem()
用于在播放列表中导航
绑定到播放器的 UI 组件(例如 PlayerView
或 PlayerControlView
)将相应地更新。
释放播放器
播放可能需要有限供应的资源,例如视频解码器,因此在不再需要播放器时,调用 release()
释放资源非常重要。
如果您的播放器位于 Activity
或 Fragment
中,请在 API 级别 24 及更高版本上的 onStop()
生命周期方法或 API 级别 23 及以下版本上的 onPause()
方法中释放播放器。对于位于 Service
中的播放器,您可以在 onDestroy()
中释放它。
使用媒体会话管理播放
在 Android 上,媒体会话提供了一种标准化的方法,用于跨进程边界与媒体播放器交互。将媒体会话连接到您的播放器允许您在外部宣传您的媒体播放,并接收来自外部来源的播放命令,例如与移动设备和大型屏幕设备上的 系统媒体控件 集成。
要使用媒体会话,请添加对 Media3 Session 模块的依赖项
implementation "androidx.media3:media3-session:1.4.1"
创建媒体会话
您可以在初始化播放器后创建 MediaSession
,如下所示
Kotlin
val player = ExoPlayer.Builder(context).build() val mediaSession = MediaSession.Builder(context, player).build()
Java
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
的类,并在 onCreate()
生命周期方法中实例化您的 MediaSession
。
Kotlin
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() } }
Java
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(); } }
在您的清单中,使用 MediaSessionService
意图过滤器注册您的 Service
类,并请求 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
以拒绝请求。
Kotlin
// This example always accepts the connection request override fun onGetSession( controllerInfo: MediaSession.ControllerInfo ): MediaSession? = mediaSession
Java
@Override public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) { // This example always accepts the connection request return mediaSession; }
连接到您的 UI
现在您的媒体会话位于与播放器 UI 所在的 Activity
或 Fragment
分开的 Service
中,您可以使用 MediaController
将它们连接在一起。在包含 UI 的 Activity
或 Fragment
的 onStart()
方法中,为您的 MediaSession
创建一个 SessionToken
,然后使用 SessionToken
来构建 MediaController
。构建 MediaController
是异步进行的。
Kotlin
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() ) }
Java
@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
时(例如 Activity
的 onStop()
生命周期方法)释放它,方法是调用 MediaController.releaseFuture()
。
发布通知
前台服务需要在活动时发布通知。MediaSessionService
将自动为您创建一个 MediaStyle
通知,形式为 MediaNotification
。要提供自定义通知,请使用 DefaultMediaNotificationProvider.Builder
创建一个 MediaNotification.Provider
,或通过创建提供程序接口的自定义实现。使用 setMediaNotificationProvider
将您的提供程序添加到 MediaSession
。
宣传您的内容库
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
的意图过滤器。此附加的意图过滤器使使用MediaBrowserCompat
API 的客户端应用能够识别您的Service
。
MediaLibrarySession
允许您以树状结构提供内容库,其中包含单个根MediaItem
。树中的每个MediaItem
可以拥有任意数量的子MediaItem
节点。您可以根据客户端应用的请求提供不同的根或不同的树。例如,您返回给寻找推荐媒体项目列表的客户端的树可能只包含根MediaItem
和单个级别的子MediaItem
节点,而您返回给其他客户端应用的树可能表示更完整的内容库。
创建MediaLibrarySession
MediaLibrarySession
扩展了MediaSession
API 以添加内容浏览 API。与MediaSession
回调相比,MediaLibrarySession
回调添加了诸如以下方法:
onGetLibraryRoot()
用于客户端请求内容树的根MediaItem
时onGetChildren()
用于客户端请求内容树中MediaItem
的子项时onGetSearchResult()
用于客户端请求给定查询的内容树的搜索结果时
相关的回调方法将包含一个LibraryParams
对象,其中包含有关客户端应用感兴趣的内容树类型的附加信号。