Android Media3 中的 MediaSession 框架
MediaSession 是构建媒体应用(尤其是那些需要在后台播放、响应媒体按钮、以及与其他应用和系统组件交互的应用)的核心组件。
1. 框架设计 (Framework Design)
Media3 中的 MediaSession 框架是 Jetpack Media3 库的一部分,它是在旧版 android.media.session.MediaSession
(API 21+) 和 androidx.media.session.MediaSessionCompat
的基础上进行了现代化和简化的设计。其核心设计目标是:
- 解耦与模块化: Media3 将媒体播放的各个方面(如 UI、播放逻辑、媒体资源管理)分离开来。MediaSession 主要负责声明应用的媒体播放状态并接收来自外部的播放控制命令。
- 单一事实来源 (Single Source of Truth): MediaSession 作为应用媒体状态的中心枢纽。它向 Android 系统和其他应用广播当前的播放信息(元数据、播放状态、播放队列等),并接收来自它们的控制指令。
- 与
Player
接口的集成: Media3 的核心是androidx.media3.common.Player
接口。MediaSession 通常与一个实现了Player
接口的播放器实例(如ExoPlayer
)协同工作。Player
负责实际的媒体解码和渲染,而 MediaSession 则将Player
的状态和控制暴露给外部。 - 向后兼容性: 虽然 Media3 是新的库,但它在设计时考虑了与旧版 Android 系统和其他媒体应用的兼容性。它在底层仍然会利用系统提供的 MediaSession 机制。
- 简化生命周期管理: Media3 旨在简化与 Android 组件生命周期(如 Service、Activity)的集成。
- 易于测试: 模块化的设计使得对媒体逻辑进行单元测试和集成测试更加容易。
核心组件和概念:
MediaSession
:- 核心职责:
- 向系统和其他应用报告应用的媒体播放状态(正在播放什么、播放进度、可用的控制命令等)。
- 接收来自系统(如媒体按钮、Google Assistant)、其他应用或蓝牙设备的播放控制命令。
- 管理播放队列。
- 关联对象: 通常与一个
Player
实例关联。它通过监听Player
的事件来更新自身状态,并将外部命令转发给Player
。
- 核心职责:
Player
(例如ExoPlayer
):- 负责媒体内容的加载、解码、渲染和播放控制(播放、暂停、快进、快退等)。
- MediaSession 并不直接处理媒体数据流,而是依赖
Player
来完成。
MediaSession.Callback
:- 一个接口,允许应用自定义如何响应来自控制器(Controller)的媒体命令。例如,当用户按下“播放”按钮时,
Callback
中的onPlay()
方法会被调用。 - 开发者可以实现此回调来拦截、修改或处理特定的播放命令,然后再(或不)将其委托给
Player
。
- 一个接口,允许应用自定义如何响应来自控制器(Controller)的媒体命令。例如,当用户按下“播放”按钮时,
MediaController
:- 其他应用或系统组件用来控制
MediaSession
的客户端。 - 它可以发现正在运行的
MediaSession
,获取其状态,并向其发送播放命令。 - 在 Media3 中,你也可以在应用内部使用
MediaController
与你自己的MediaSession
进行交互,例如从 Activity 控制后台播放服务中的MediaSession
。
- 其他应用或系统组件用来控制
MediaSessionService
:- 一个专门用于托管
MediaSession
和Player
的 AndroidService
。 - 这是推荐的在后台运行媒体播放的方式,确保即使应用 UI 不可见,播放也能继续。
- 它会自动处理服务的生命周期、前台服务管理(对于 Android O 及更高版本至关重要,以避免后台播放被终止)以及媒体按钮接收者的注册。
- 一个专门用于托管
MediaLibraryService
(可选,但推荐用于媒体浏览):MediaSessionService
的一个子类,增加了媒体浏览功能。- 它允许客户端(如 Android Auto、Wear OS 或其他媒体浏览应用)查询和浏览你的应用提供的媒体内容库。
- 通过实现
MediaLibrarySession.Callback
中的onGetLibraryRoot()
,onGetChildren()
,onGetItem()
等方法来提供媒体库结构。
MediaNotificationManager
/PlayerNotificationManager
:- Media3 提供了工具来简化媒体播放通知的创建和管理。
- 这些通知会显示当前播放的元数据、播放控件,并与
MediaSession
的状态同步。 PlayerNotificationManager
(来自 ExoPlayer 旧版,但在 Media3 中仍可找到类似概念或通过MediaSessionService
自动处理)负责将Player
的状态转换成一个持续的通知。MediaSessionService
内部集成了通知管理。
数据流和交互模型:
- 初始化:
- 应用创建一个
Player
实例 (如ExoPlayer
)。 - 应用创建一个
MediaSession
,并将Player
实例与之关联。 - 如果需要后台播放,
MediaSession
和Player
通常托管在MediaSessionService
中。
- 应用创建一个
- 状态广播:
- 当
Player
的状态发生变化(例如,开始播放、暂停、切换曲目、加载元数据),它会通知关联的MediaSession
。 MediaSession
更新其内部状态,并通过 Android 系统的媒体子系统将这些状态(如PlaybackStateCompat
,MediaMetadataCompat
)广播出去。
- 当
- 命令接收:
- 系统或其他应用的
MediaController
可以发现此MediaSession
。 - 用户通过媒体按钮(耳机、蓝牙设备)、系统 UI(如通知栏、锁屏控件)或语音命令(如 Google Assistant)与媒体进行交互。
- 这些交互被转换成媒体命令,发送到应用的
MediaSession
。
- 系统或其他应用的
- 命令处理:
MediaSession
接收到命令。- 如果设置了
MediaSession.Callback
,相应的回调方法会被调用。 - 在回调方法中,应用可以决定如何处理该命令。通常,这些命令会被委托给关联的
Player
实例执行(例如,调用player.play()
或player.pause()
)。
- UI 更新 (可选,应用内):
- 如果应用的 UI (如 Activity 或 Fragment) 需要显示播放状态或控制播放,它也可以创建一个
MediaController
来连接到同一个MediaSession
(即使是在同一个应用内),从而接收状态更新并发送命令。
- 如果应用的 UI (如 Activity 或 Fragment) 需要显示播放状态或控制播放,它也可以创建一个
这种设计使得媒体播放逻辑与 UI 和系统交互清晰分离,提高了代码的可维护性和可测试性。
2. 框架使用 (Framework Usage)
使用 Media3 中的 MediaSession 框架通常涉及以下步骤:
步骤 1: 添加依赖
在你的 build.gradle
(Module: app) 文件中添加 Media3 的相关依赖:
Gradle
dependencies { // 核心 Media3 库,包含 Player 接口, ExoPlayer, MediaSession 等 implementation "androidx.media3:media3-session:1.3.1" // 使用最新版本 implementation "androidx.media3:media3-exoplayer:1.3.1" implementation "androidx.media3:media3-ui:1.3.1" // 如果需要内置的 UI 组件 // 如果需要 HLS, DASH, SmoothStreaming 支持,可以添加相应的 ExoPlayer 模块 implementation "androidx.media3:media3-exoplayer-hls:1.3.1" implementation "androidx.media3:media3-exoplayer-dash:1.3.1" }
请注意检查并使用最新的 Media3 版本。
步骤 2: 创建 Player 实例
通常使用 ExoPlayer
:
Java
// 在你的 Service 或 Activity 中 import androidx.media3.exoplayer.ExoPlayer; Player player = new ExoPlayer.Builder(context).build();
步骤 3: 创建 MediaSession
Java
// 在你的 Service 或 Activity 中 import androidx.media3.session.MediaSession; // 1. 创建 MediaSession MediaSession mediaSession = new MediaSession.Builder(context, player) .setId("my_awesome_music_session") // 可选,但推荐用于调试 // .setSessionActivity(pendingIntentToOpenActivity) // 用户点击通知时打开的 PendingIntent // .setCallback(new MyMediaSessionCallback()) // 可选,用于自定义命令处理 .build();
context
: 通常是ApplicationContext
或Service
context。player
: 你在步骤 2 中创建的Player
实例。setId()
: 为会话设置一个唯一的 ID,方便调试。setSessionActivity()
: 设置一个PendingIntent
,当用户从通知或其他地方选择你的会话时,会启动这个 Intent(通常是打开你的播放器 UI Activity)。setCallback()
: (可选但常用) 设置一个MediaSession.Callback
来处理自定义命令或在命令执行前后执行额外逻辑。
步骤 4: (推荐) 使用 MediaSessionService 进行后台播放
这是管理后台播放、媒体按钮和通知的推荐方式。
- 创建
MyMediaSessionService.java
:
Java
import androidx.media3.common.AudioAttributes; import androidx.media3.common.C; import androidx.media3.exoplayer.ExoPlayer; import androidx.media3.session.MediaSession; import androidx.media3.session.MediaSessionService; public class MyMediaSessionService extends MediaSessionService { private MediaSession mediaSession = null; private Player player = null; @Override public void onCreate() { super.onCreate(); initializePlayerAndSession(); } private void initializePlayerAndSession() { player = new ExoPlayer.Builder(this).build(); // 设置音频属性,例如用途和内容类型,这对于音频焦点管理很重要 AudioAttributes audioAttributes = new AudioAttributes.Builder() .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC) .setUsage(C.USAGE_MEDIA) .build(); player.setAudioAttributes(audioAttributes, true /* handleAudioFocus */); // true 表示让 ExoPlayer 自动处理音频焦点 mediaSession = new MediaSession.Builder(this, player) .setCallback(new MyMediaSessionCallback()) // 可选的回调 .build(); } @Override public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) { // 返回 MediaSession 实例,允许客户端连接 return mediaSession; } // (可选) 自定义回调 private class MyMediaSessionCallback implements MediaSession.Callback { // 在这里重写需要自定义处理的命令,例如 onPlay, onPause, onSkipToNext 等 // 例如:在播放前检查网络,或者记录分析数据 } @Override public void onDestroy() { if (player != null) { player.release(); player = null; } if (mediaSession != null) { mediaSession.release(); mediaSession = null; } super.onDestroy(); } }
- 在
AndroidManifest.xml
中声明 Service:
XML
<service android:name=".MyMediaSessionService" android:foregroundServiceType="mediaPlayback" android:exported="true"> <intent-filter> <action android:name="androidx.media3.session.MediaSessionService"/> <action android:name="android.media.browse.MediaBrowserService"/> </intent-filter> </service>
foregroundServiceType=”mediaPlayback” 对于 Android 9 (API 28) 及更高版本是必需的。
exported=”true” 允许其他应用连接到你的服务。
步骤 5: 管理 Player 和 MediaSession 的生命周期
- 初始化: 在
Service.onCreate()
或Activity.onCreate()
中创建和设置Player
和MediaSession
。 - 释放资源: 在
Service.onDestroy()
或Activity.onDestroy()
中调用player.release()
和mediaSession.release()
。这非常重要,以避免资源泄漏。如果MediaSession
与Player
关联,通常释放Player
会自动更新MediaSession
的状态,但显式释放两者是好习惯。MediaSessionService
会在适当的时候帮助管理这些。
步骤 6: 加载和播放媒体
使用 Player
接口的方法来控制播放:
Java
// 假设 player 是你的 ExoPlayer 实例 import androidx.media3.common.MediaItem; // 创建一个 MediaItem MediaItem mediaItem = MediaItem.fromUri("YOUR_MEDIA_URI_HERE"); // 例如 http://, file://, asset:// // 将 MediaItem 添加到播放列表并准备播放 player.setMediaItem(mediaItem); player.prepare(); // 异步准备 // 开始播放 (可以在准备完成后,或者用户请求时) player.play(); // 或者 player.setPlayWhenReady(true); // 其他控制: // player.pause(); // player.seekTo(positionMs); // player.setRepeatMode(Player.REPEAT_MODE_ONE / Player.REPEAT_MODE_ALL / Player.REPEAT_MODE_OFF); // player.setShuffleModeEnabled(true/false);
步骤 7: (可选) 从 Activity/Fragment 控制 MediaSessionService
如果你的 MediaSession
托管在 MediaSessionService
中,你的 UI (Activity/Fragment) 可以使用 MediaController
来与之通信。
Java
// 在你的 Activity 或 Fragment 中 import androidx.media3.session.MediaController; import androidx.media3.session.SessionToken; import android.content.ComponentName; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import java.util.concurrent.ExecutionException; // ... SessionToken sessionToken = new SessionToken(this, new ComponentName(this, MyMediaSessionService.class)); ListenableFuture<MediaController> controllerFuture = new MediaController.Builder(this, sessionToken).buildAsync(); controllerFuture.addListener( () -> { try { MediaController mediaController = controllerFuture.get(); // 现在你可以使用 mediaController 来控制播放和监听状态变化 // 例如: mediaController.play(), mediaController.pause() // mediaController.addListener(new Player.Listener() { ... }); // 在不需要时释放: mediaController.release(); } catch (ExecutionException | InterruptedException e) { // 处理错误 } }, MoreExecutors.directExecutor() // 或者 ContextCompat.getMainExecutor(this) ); // 记得在 Activity/Fragment 销毁时释放 controller // private MediaController mediaController; // @Override // protected void onDestroy() { // if (mediaController != null) { // MediaController.releaseFuture(controllerFuture); // 或 mediaController.release() 如果已经获取 // } // super.onDestroy(); // }
步骤 8: 处理通知
当你使用 MediaSessionService
时,它会自动为你处理媒体通知的创建和更新。通知会显示当前的媒体元数据和播放控件。你可以通过 MediaSessionService.setMediaNotificationProvider()
来自定义通知的外观和行为,或者直接让其使用默认提供者。
如果未使用 MediaSessionService
,你可能需要使用 PlayerNotificationManager
(来自 androidx.media3:media3-ui
包) 手动创建和管理通知,并将其与 MediaSession
同步。
3. 框架提供的自定义能力 (Customization Capabilities)
Media3 MediaSession 框架提供了广泛的自定义能力,允许开发者根据具体需求调整其行为:
MediaSession.Callback
:- 拦截和处理媒体命令: 这是最核心的自定义点。你可以重写
Callback
中的方法(如onPlay
,onPause
,onSkipToNext
,onSeekTo
,onSetRating
,onCustomCommand
等)来改变默认行为。- 示例:
- 在
onPlay()
中检查用户是否拥有播放权限。 - 在
onSkipToNext()
中实现自定义的下一曲目选择逻辑(例如,基于用户喜好推荐)。 - 发送分析事件记录用户的播放行为。
- 在实际执行播放前加载广告。
- 在
- 示例:
- 自定义命令 (Custom Commands): 通过
onCustomCommand
(发送方) 和MediaSession.Callback.onCustomCommand
(接收方),应用可以定义和处理标准媒体命令之外的自定义操作。例如,切换音效、添加到收藏夹、调整播放速度的特定档位等。 - 修改播放队列:
onAddMediaItems
,onSetMediaItems
,onSetShuffleModeEnabled
,onSetRepeatMode
等回调允许你控制播放队列的修改方式。
- 拦截和处理媒体命令: 这是最核心的自定义点。你可以重写
- 自定义
Player
行为:- 由于
MediaSession
与Player
接口解耦,你可以使用任何实现了Player
接口的播放器。虽然ExoPlayer
是最常用的,但理论上你可以实现自己的Player
。 - 对于
ExoPlayer
本身,它提供了大量的自定义选项:- 自定义
LoadControl
来改变缓冲策略。 - 自定义
RenderersFactory
来使用自定义的音频/视频渲染器。 - 添加
AnalyticsListener
来接收详细的播放事件。 - 配置
DataSource.Factory
来从各种来源加载媒体(网络、本地文件、自定义服务器)。 - 处理 DRM (数字版权管理)。
- 自定义
- 由于
- 通知自定义 (
MediaNotificationProvider
):- 当使用
MediaSessionService
时,它使用一个MediaNotificationProvider
来创建和更新媒体通知。 - 你可以提供自己的
MediaNotificationProvider
实现来完全控制通知的外观和行为:- 自定义通知图标、颜色、布局(部分受系统限制)。
- 自定义通知中的操作按钮(例如,添加“喜欢”、“不喜欢”按钮)。
- 修改通知的优先级和渠道。
DefaultMediaNotificationProvider
本身也提供了一些构造函数参数来自定义常用选项,如通知图标。
- 当使用
- 媒体浏览 (
MediaLibraryService
和MediaLibrarySession.Callback
):- 如果你希望你的应用内容能被其他应用(如 Android Auto, Wear OS, Google Assistant)浏览和播放,你需要使用
MediaLibraryService
。 - 通过实现
MediaLibrarySession.Callback
的方法,你可以定义你的媒体库结构:onGetLibraryRoot()
: 返回媒体库的根节点。onGetChildren()
: 返回指定父媒体项下的子媒体项列表(例如,专辑下的歌曲列表,播放列表中的曲目)。onGetItem()
: 返回单个媒体项的详细信息。onSearch()
: (如果支持) 实现搜索逻辑。
- 这允许你以树状结构或自定义结构组织你的音乐、播客等内容。
- 如果你希望你的应用内容能被其他应用(如 Android Auto, Wear OS, Google Assistant)浏览和播放,你需要使用
- 广播自定义元数据和播放状态:
- 你可以通过
Player.setMediaItems()
设置包含丰富元数据(标题、艺术家、专辑封面 URI、时长等)的MediaItem
。MediaSession
会将这些信息广播出去。 - 通过
MediaSession.setCustomLayout()
,你可以为连接的控制器(如 Android Auto)提供一组自定义操作按钮的布局。 MediaSession.setSessionExtras()
允许你附加一个Bundle
的额外数据到MediaSession
,供MediaController
读取。
- 你可以通过
- 可用命令 (Available Commands):
MediaSession
维护一组当前可用的播放命令(Player.Commands
)。例如,如果当前没有下一首歌曲,则COMMAND_SEEK_TO_NEXT_MEDIA_ITEM
可能不可用。- 你可以通过
MediaSession.Builder.setAvailableCommandsProvider()
或在MediaSession.Callback
中动态修改这些可用命令,以精确控制远程控制器可以执行哪些操作。 Player
本身也会根据其状态(例如,是否有可寻找的内容)更新其可用命令,MediaSession
会反映这些。
- 连接特定控制器 (
MediaSession.Callback.onConnect
):- 当一个新的
MediaController
尝试连接到你的MediaSession
时,onConnect
回调会被调用。 - 你可以在这里决定是否接受连接(例如,基于包名进行白名单/黑名单验证),或者为特定的控制器配置不同的可用命令或自定义布局。
- 当一个新的
- 错误处理和报告:
Player.Listener.onPlayerError
可以用来监听播放器错误。- 你可以在
MediaSession.Callback
中处理这些错误,例如向用户显示友好的错误消息,或者尝试恢复播放。
通过组合这些自定义点,开发者可以构建出功能丰富且高度定制化的媒体播放体验。
4. 开发音乐 App 会用到的框架能力 (Application in a Music App)
对于开发一款音乐 App,你会广泛地使用 Media3 MediaSession 框架的多种能力:
- 核心播放控制 (
Player
+MediaSession
):ExoPlayer
: 用于实际的音乐文件(MP3, AAC, FLAC, Ogg Vorbis 等)的解码和播放,支持网络流(HTTP/HTTPS, HLS, DASH)、本地文件。MediaSession
:- 将
ExoPlayer
实例关联,以声明播放状态(正在播放、暂停、缓冲、当前歌曲、进度)。 - 接收来自系统(通知栏、锁屏、蓝牙耳机按钮、Android Auto、Wear OS、Google Assistant)的播放/暂停/上一首/下一首/快进/快退命令,并将它们转发给
ExoPlayer
。
- 将
- 后台播放 (
MediaSessionService
):- 至关重要: 音乐通常需要在应用 UI 不可见时继续播放。
MediaSessionService
确保播放可以在后台可靠运行,并自动管理前台服务状态以防止被系统终止。 - 它还会处理媒体按钮事件的接收。
- 至关重要: 音乐通常需要在应用 UI 不可见时继续播放。
- 媒体元数据和封面 (
MediaItem
和MediaMetadata
):- 为每首歌曲创建
MediaItem
,并通过MediaMetadata.Builder
设置丰富的元数据:setTitle()
: 歌曲名称setArtist()
: 艺术家setAlbumTitle()
: 专辑名称setAlbumArtist()
: 专辑艺术家setArtworkUri()
/setArtworkData()
: 专辑封面(URI 或 byte array)setTrackNumber()
: 音轨号setTotalTrackCount()
: 专辑总音轨数setReleaseDate()
: 发行日期setGenre()
: 音乐流派setUserRating()
/setOverallRating()
: 用户评分setExtras()
: 存储其他自定义信息,如歌词 URI、音质等。
MediaSession
会将这些元数据广播给系统,用于在通知、锁屏、蓝牙设备上显示。
- 为每首歌曲创建
- 播放队列管理 (
Player
接口方法,MediaSession.Callback
):- 使用
player.addMediaItem(s)
,player.removeMediaItem(s)
,player.moveMediaItem(s)
等方法管理播放队列。 - 用户可能希望对播放列表进行排序、添加歌曲、移除歌曲。这些操作会更新
Player
的播放列表,MediaSession
会反映这些变化。 - 实现
MediaSession.Callback
中的onAddMediaItems
,onSetMediaItems
等方法,可以让你在这些操作发生时执行额外逻辑(如保存播放列表状态)。 - 支持随机播放 (
player.setShuffleModeEnabled(true/false)
) 和重复播放 (player.setRepeatMode(Player.REPEAT_MODE_OFF/ONE/ALL)
).
- 使用
- 媒体通知 (
MediaSessionService
或PlayerNotificationManager
):MediaSessionService
会自动创建和更新一个包含当前歌曲信息和播放控件的通知。- 自定义通知:
- 通过
DefaultMediaNotificationProvider.Builder
或自定义MediaNotificationProvider
来设置应用图标、通知颜色。 - 添加自定义操作按钮,如“喜欢”、“添加到播放列表”、“查看歌词”等(通过
MediaSession.Callback.onCustomCommand
和MediaNotificationProvider.getActions()
)。
- 通过
- 媒体浏览 (
MediaLibraryService
和MediaLibrarySession.Callback
):- 非常推荐: 允许用户通过 Android Auto、Wear OS、Google Assistant 或其他媒体浏览应用来浏览和播放你的音乐库。
- 实现
MediaLibrarySession.Callback
:onGetLibraryRoot()
: 返回根目录(例如,”我的音乐库”)。onGetChildren()
:- 返回不同分类的入口,如“专辑”、“艺术家”、“播放列表”、“流派”、“所有歌曲”。
- 对于“专辑”节点,返回该专辑下的所有歌曲。
- 对于“艺术家”节点,返回该艺术家的所有专辑或歌曲。
onGetItem()
: 返回特定歌曲的MediaItem
(包含所有元数据)。(可选) onSearch()
: 实现音乐库内搜索功能。
- 这使得你的音乐 App 能够很好地融入 Android 生态系统。
- 音频焦点管理:
ExoPlayer
可以通过player.setAudioAttributes(audioAttributes, true /* handleAudioFocus */)
自动处理音频焦点。- 当其他应用请求音频焦点时(例如,导航应用开始播报方向),你的音乐播放会自动暂停或降低音量(ducking)。当焦点返回时,播放会自动恢复。
- 正确处理音频焦点对于良好的用户体验至关重要。
- 媒体按钮响应 (耳机、蓝牙设备):
MediaSessionService
会自动注册一个媒体按钮接收器。当用户按下耳机或蓝牙设备上的播放/暂停/下一首/上一首按钮时,这些事件会被路由到你的MediaSession
,进而控制ExoPlayer
。
- 锁屏控件:
- 当
MediaSession
处于活动状态并正在播放媒体时,Android 系统会在锁屏界面显示媒体控件(歌曲信息、封面、播放控制按钮)。这是由系统自动处理的,只要你的MediaSession
正确发布了状态和元数据。
- 当
- 自定义命令和操作 (
MediaSession.Callback.onCustomCommand
):- “喜欢/不喜欢”歌曲: 发送自定义命令,在
Callback
中处理,更新歌曲的喜好状态,并可能更新 UI 和服务器数据。 - 添加到播放列表: 自定义命令,在
Callback
中弹出对话框让用户选择播放列表,或直接添加到默认列表。 - 调整播放速度/音高: 虽然
ExoPlayer
支持setPlaybackParameters
,但你可能希望通过自定义命令提供预设的速度选项。 - 查看歌词: 自定义命令,触发在 UI 中显示歌词的逻辑。
- 下载歌曲: 自定义命令,触发歌曲下载逻辑。
- “喜欢/不喜欢”歌曲: 发送自定义命令,在
- 错误处理和报告:
- 监听
Player.Listener.onPlayerError
来捕获播放错误(如网络问题、文件损坏、解码错误)。 - 在
MediaSession.Callback
或Player.Listener
中处理这些错误,例如:- 向用户显示友好的错误消息。
- 尝试重新连接或播放下一首歌曲。
- 记录错误以供分析。
- 监听
- 与 UI (Activity/Fragment) 的交互 (
MediaController
):- UI 组件使用
MediaController
连接到MediaSessionService
中的MediaSession
。 - 监听状态: 监听
Player.Listener
事件(通过MediaController
)来更新 UI 上的播放进度条、歌曲信息、播放/暂停按钮状态、封面等。 - 发送命令: UI 上的按钮(播放、暂停、下一首、拖动进度条)通过
MediaController
向MediaSession
发送命令。
- UI 组件使用
- 最近播放/播放历史:
- 在
MediaSession.Callback
的onPlay
或Player.Listener.onMediaItemTransition
中记录播放的歌曲,用于实现“最近播放”功能。
- 在
- Android Auto / Wear OS 集成:
- 通过正确实现
MediaLibraryService
,你的音乐应用可以无缝地在 Android Auto 和 Wear OS 设备上提供浏览和播放体验。 MediaSession
还会将播放状态和元数据同步到这些设备。
- 通过正确实现
通过有效地利用这些 Media3 MediaSession 框架的能力,你可以构建一个功能强大、用户体验良好且与 Android 生态系统深度集成的音乐应用。记住,MediaSessionService
和 MediaLibraryService
(如果需要浏览) 是现代音乐应用的关键组件。