android 媒体之 MediaSession

一、框架

1. MediaSession (媒体会话)

• 角色: 核心枢纽和状态机。它是整个媒体播放控制的核心。

• 作用:

bash 复制代码
封装播放状态: 持有当前播放状态(如播放/暂停、当前播放位置、播放速度、播放队列、当前媒体项元数据等)。

暴露控制接口: 通过 setCallback() 设置一个 MediaSession.Callback。这个回调定义了如何处理来自外部的控制命令(如 onPlay(), onPause(),
onSkipToNext(), onSeekTo() 等)。应用的后台播放逻辑主要实现在这里。

分发状态更新: 当播放状态发生变化(如开始播放、暂停、切歌、进度更新)时,MediaSession 会主动通知所有连接到它的 MediaController。

提供令牌 (Token): 生成一个 MediaSession.Token。这个 Token 是 MediaSession的唯一标识符和安全凭证,用于前台组件(通过 MediaBrowser)连接到后台服务并获取 MediaController。

• 位置: 通常由后台服务(MediaBrowserService)创建并持有。

• 关键点: 它不直接处理媒体播放,而是定义接口和保存状态,具体的播放动作由 MediaSession.Callback 的实现者(你的播放逻辑)完成。

2. MediaBrowserService (媒体浏览器服务)

• 角色: 后台服务的基类。它是长期运行在后台(通常在一个独立的进程)的服务,负责管理媒体内容库、播放逻辑以及与前台 UI 的连接。

• 作用:

bash 复制代码
生命周期管理: 作为 Android Service,它可以在 UI 不可见时(如应用退到后台、锁屏)继续运行,保证播放不中断。

托管 MediaSession: 创建并持有 MediaSession 实例,实现其 Callback 中的播放控制逻辑。

管理内容库: 实现 onLoadChildren() 等方法,向连接的客户端(MediaBrowser)提供媒体内容的结构(如专辑列表、歌曲列表)。

连接点: 提供 onGetRoot() 方法,验证请求连接的客户端(MediaBrowser)是否有权限,并返回一个 BrowserRoot 对象(通常包含 MediaSession 的 Token)。这是前台连接后台的入口。

管理客户端连接: 维护连接到它的 MediaBrowser 客户端列表。

• 位置: 后台服务,继承自 Service。

• 关键点: 它是后台播放逻辑的容器和连接桥梁。它创建 MediaSession 并实现其核心播放逻辑。

3. MediaBrowser (媒体浏览器)

• 角色: 前台 UI 的连接器。由前台组件(如 Activity、Fragment)创建和使用。

• 作用:

bash 复制代码
连接到 MediaBrowserService: 使用 MediaBrowserService 的组件名(或通过 MediaSession.Token)发起连接请求。

获取会话令牌: 连接成功后,通过 getSessionToken() 方法获取到 MediaBrowserService 所持有的 MediaSession 的 Token。

浏览媒体内容: 使用 subscribe() 方法订阅媒体内容树(如"所有歌曲"、"按艺术家")的更新,并通过 MediaBrowser.SubscriptionCallback 接收内容(MediaItem 列表)。使用 unsubscribe() 取消订阅。

断开连接: 在 UI 销毁时断开与服务的连接。

• 位置: 前台 UI 组件(Activity/Fragment)。

• 关键点: 它主要负责建立连接和获取内容结构。它本身不直接控制播放。获取到 MediaSession.Token 后,它的主要任务就完成了(用于创建 MediaController)。

4. MediaController (媒体控制器)

• 角色: 前台 UI 的控制代理和状态接收器。

• 作用:

bash 复制代码
创建: 前台 UI 组件使用 MediaBrowser 连接成功后获取到的 MediaSession.Token,通过 MediaControllerCompat.getMediaController() 或 MediaControllerCompat 的构造函数创建 MediaController。

发送控制命令: 提供方法(如 getTransportControls().play(), pause(), skipToNext(), seekTo())让 UI 发送播放控制命令。这些命令最终会被路由到后台 MediaSession 的 Callback 中执行。

获取当前状态: 提供方法(如 getPlaybackState(), getMetadata())让 UI 查询当前的播放状态和媒体元数据。

接收状态更新: 通过注册 MediaController.Callback,UI 可以监听 MediaSession 的状态变化(如播放状态改变、元数据更新、队列变化)。当后台状态变化时,MediaSession 会通知所有关联的 MediaController,这些 MediaController 再回调其注册的 Callback 通知 UI 更新。

与 MediaSession 交互: 它是前台 UI 与后台 MediaSession 交互的主要通道。

• 位置: 前台 UI 组件(Activity/Fragment),通常与 MediaBrowser 在同一组件中使用。

• 关键点: 它是 UI 控制播放和获取状态更新的主要工具。它通过 MediaSession.Token 与后台的 MediaSession 建立联系。

5.关系和工作流程

  1. 后台启动: 你的应用启动后台服务(继承自 MediaBrowserService)。该服务在 onCreate() 中创建 MediaSession,设置其 Callback(实现播放逻辑),并可能开始准备播放。
  2. 前台启动: 用户打开应用 UI(Activity/Fragment)。
  3. 连接: UI 创建 MediaBrowser 对象,使用 MediaBrowserService 的组件名调用 connect() 尝试连接。
  4. 验证与连接: MediaBrowserService 的 onGetRoot() 被调用,验证客户端权限并返回 BrowserRoot(包含 MediaSession.Token)。连接成功。
  5. 获取控制器: UI 的 MediaBrowser.ConnectionCallback.onConnected() 被调用。在这里,UI 通过 MediaBrowser.getSessionToken() 获取 MediaSession.Token,并用它创建 MediaController。
  6. 浏览内容 (可选): UI 可以使用 MediaBrowser.subscribe() 请求媒体内容结构(如显示歌曲列表)。
  7. 控制播放: 当用户点击 UI 上的播放按钮时,UI 调用 MediaController.getTransportControls().play()。
  8. 命令路由: MediaController 将 play() 命令发送给后台的 MediaSession。
  9. 执行命令: MediaSession 调用其 Callback.onPlay() 方法。你在这里实现的后台播放逻辑开始执行(例如,开始播放音乐)。
  10. 状态更新: 播放开始后,后台逻辑(在 Callback.onPlay() 中)更新 MediaSession 的状态(例如,设置播放状态为 PlaybackState.STATE_PLAYING)。
  11. 状态分发: MediaSession 自动将新的播放状态广播给所有连接的 MediaController。
  12. UI 更新: UI 的 MediaController 收到状态更新,触发其注册的 MediaController.Callback.onPlaybackStateChanged()。UI 在这里更新界面(如将按钮图标改为暂停图标,更新进度条)。
  13. 断开: 当 UI 销毁时,它调用 MediaController.unregisterCallback() 和 MediaBrowser.disconnect() 进行清理。

注意点

• MediaSession: 核心状态和控制接口。定义"能做什么"(Callback)和"当前状态是什么"。

• MediaBrowserService: 后台服务的基类。托管 MediaSession,实现播放逻辑,管理内容库,处理客户端连接。

• MediaBrowser: 前台连接器。负责连接到 MediaBrowserService 并获取 MediaSession.Token(用于创建 MediaController)和浏览内容。

• MediaController: 前台代理。利用 MediaSession.Token 与后台 MediaSession 通信,发送控制命令并接收状态更新,驱动 UI 变化。

相关推荐
lizhenjun1141 天前
android修改线程名字长度
android
用户69371750013841 天前
Google 正在“收紧侧加载”:陌生 APK 安装或需等待 24 小时
android·前端
用户69371750013841 天前
Room 3.0:这次不是升级,是重来
android·前端·google
alexhilton1 天前
Compose中的ContentScale:终极可视化指南
android·kotlin·android jetpack
腾阳1 天前
99%的人忽视了这一点:活着本身就是人生的意义,别让抑郁和内耗成为你的枷锁!
经验分享·程序人生·职场和发展·跳槽·学习方法·媒体
Digitally2 天前
2026 年 8 款安卓数据擦除软件和应用对比
android
杨忆2 天前
android 11以上 截图工具类
android
粤M温同学2 天前
Android Studio 中安装 CodeBuddy AI助手
android·ide·android studio
阿拉斯攀登2 天前
【RK3576 安卓 JNI/NDK 系列 08】RK3576 实战(二):JNI 调用 I2C 驱动读取传感器数据
android·安卓ndk入门·jni方法签名·java调用c++·rk3576底层开发·rk3576 i2c开发
赶路人儿2 天前
常见的mcp配置
android·adb