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或Servicecontext。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 (如果需要浏览) 是现代音乐应用的关键组件。