一、框架

在这里插入图片描述

1. MediaSession (媒体会话)

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

• 作用:

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

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

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

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

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

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

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

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

• 作用:

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

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

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

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

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

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

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

3. MediaBrowser (媒体浏览器)

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

• 作用:

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

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

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

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

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

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

4. MediaController (媒体控制器)

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

• 作用:

创建: 前台 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 变化。

二、应用

App 只要拿着自己的身份证(Context/包名)去系统那里登记,系统发给它一个号码牌(Token),绑定就完成了。

1. 绑定的核心三要素

MediaSession 与 App 的绑定依赖于三个关键动作:

  1. 实名注册 (Context & Tag): App 创建 Session 时必须传入自己的上下文(Context)。
  2. 上交信物 (Session Token): Session 创建后会生成一个唯一的令牌,系统拿着这个令牌就能找到 App。
  3. 接通线路 (Callback): App 必须设置一个“监听器”,这样系统发出的指令 App 才能收到。

2. 代码层面的绑定流程

让我们看看 App 内部的代码(以伪代码为例)是如何一步步把自己“绑”给系统的:

// 步骤 1: 创建 Session (实名注册)
// "Context" 代表了 App 自身 (比如 QQ音乐的 Service)
// "QQMusicCar" 是这个 Session 的名字 (Tag)
MediaSession session = new MediaSession(context, "QQMusicCar");

// 此时,系统服务(MediaSessionService) 已经知道:
// 有一个叫 "QQMusicCar" 的 Session,属于 "com.miui.player" 这个包(App)。

// 步骤 2: 设置监听 (接通线路)
// 告诉系统:如果有人按暂停/播放,请调用这个 Callback 里的方法
session.setCallback(new MediaSession.Callback() {
    @Override
    public void onPlay() {
        // App 收到指令,开始播放
    }
    @Override
    public void onPause() {
        // App 收到指令,开始暂停
    }
});

// 步骤 3: 激活 (告诉系统我准备好了)
session.setActive(true);

// 步骤 4: 获取 Token (信物)
// 这个 Token 是系统用来识别这个 Session 的唯一标识
MediaSession.Token token = session.getSessionToken();

更多推荐