一,前期基础知识储备
如何快速开发一款音频应用?基于已有的合作应用进行开发,比如百度的车载Carlife系统对QQ音乐,喜马拉雅等的调用。 音乐应用被第三方的Android app打开,并获取其中的歌单,曲目列表,同时控制其播放。
Spotify - 一家在线音乐流服务平台,和其合作的厂商也是采用这种实现方式,官方SDK说明如下:
The Spotify SDK allows your application to interact with the Spotify app running in the background as a service. The capabilities of this API include getting metadata for the currently playing track and context, issuing basic playback commands and initiating playback of tracks.
合作方开发一个客户端应用,与作为服务端的Spotify进行交互,获取播放条目,并控制播放状态。
通俗一点,就是自己开发一个应用,作为一个进程,Spotify播放器是一个应用,作为一个进程,然后通过客户端/服务器设计,实现一个进程控制另一个进程的播放状态。
这种实现方式就是使用Android中的MediaBrowser与MediaBrowserService,Android官方文档:
MediaBrowserService 提供两个主要功能:
- 当您使用 MediaBrowserService 时,具有 MediaBrowser 的其他组件和应用可以发现您的服务,创建自己的媒体控制器,连接到您的媒体会话,并控制播放器。Wear OS 和 Android Auto 应用才得以访问您的媒体应用。
- 此外,它还提供了一个可选的 Browsing API。应用可以不使用此功能。通过 Browsing API,客户端可以查询服务并构建其内容层次结构的表示,这可能表示播放列表、媒体库或其他类型的集合。
二,上代码,具体实现
Client需要创建MediaBrowser,Server需要实现MediaBrowserService,在建⽴连接后,两端之间的交互主要通过MediaController和MediaSession。两个类之间通过预先定义的callback进⾏交互,
MediaSession控制着播放器的播放,MediaController来控制着UI的变化。
实现具体分三步:
1. 构建媒体浏览器服务
说明了如何创建包含媒体会话的媒体浏览器服务,管理客户端连接以及在播放音频时成为前台服务。
2. 构建媒体浏览器客户端
说明了如何创建包含界面和媒体控制器的媒体浏览器客户端 Activity,以及如何与媒体浏览器服务进行连接和通信。
3. 媒体会话回调
说明了媒体会话回调方法如何管理媒体会话、媒体浏览器服务以及其他应用组件(如通知和广播接收器)。
1. 构建媒体浏览器服务
该步骤通常在播放器内部实现,客户端不必关注,客户端只需调用创建好的服务即可。
1)声明Service
必须在其清单中声明带有 Intent 过滤器的 MediaBrowserService。
<service android:name=".MediaPlaybackService">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
2)初始化媒体会话
当服务收到 onCreate() 生命周期回调方法时,它应该执行以下步骤:
- 创建并初始化媒体会话
- 设置媒体会话回调
- 设置媒体会话令牌
3)管理客户端连接
MediaBrowserService 有两个方法来处理客户端连接:onGetRoot() 控制对服务的访问,onLoadChildren() 使客户端能够构建和显示 内容层次结构菜单。
2. 构建媒体浏览器客户端
-
说明了如何创建包含界面和媒体控制器的媒体浏览器客户端 Activity
-
以及如何与媒体浏览器服务进行连接和通信。
要完成客户端/服务器设计,您必须构建包含界面代码、关联的 MediaController 和 MediaBrowser 的 Activity 组件。
MediaBrowser 执行两项重要功能:
-
连接到 MediaBrowserService;
-
并在连接后为您的界面创建 MediaController。
1)连接到 MediaBrowserService
创建客户端 Activity 后,它会连接到 MediaBrowserService。这里涉及一点握手和跳跃。修改 Activity 的生命周期回调,如下所示:
-
onCreate() 构造 MediaBrowserCompat。传入 MediaBrowserService 的名称和已定义的 MediaBrowserCompat.ConnectionCallback。
-
onStart() 连接到 MediaBrowserService。这里体现了 MediaBrowserCompat.ConnectionCallback 的神奇之处。如果连接成功,onConnect() 回调会创建媒体控制器,将其链接到媒体会话,将您的界面控件链接到 MediaController,并注册控制器以接收来自媒体会话的回调。
-
onResume() 设置音频流,以便您的应用响应设备上的音量控制。
-
onStop() 断开 MediaBrowser 的连接,并在 Activity 停止时取消注册 MediaController.Callback。
2)自定义 MediaBrowserCompat.ConnectionCallback
当您的 Activity 构造 MediaBrowserCompat 时,您必须创建 ConnectionCallback 的实例。修改其 onConnected() 方法以从 MediaBrowserService 检索媒体会话令牌,并使用该令牌创建 MediaControllerCompat。
private void connectToSession(MediaSessionCompat.Token token) throws RemoteException {
// MediaSessionCompat 与 MediaControllerCompat 关联
mMediaController = new MediaControllerCompat(mContext, token);
// 音频变化监听
mMediaController.registerCallback(mMediaControllerCallback);
// 获取TransportControls 执行play pause next pre操作
mTransportControls = mMediaController.getTransportControls();
MediaMetadataCompat metadata = mMediaController.getMetadata();
PlaybackStateCompat pbState = mediaController.getPlaybackState();
}
3)将界面连接到媒体控制器
MediaControllerCompat.TransportControls mTransportControls;
mTransportControls = mMediaController.getTransportControls();
TransportControls 方法向服务的媒体会话发送回调。确保为每个控件定义了相应的 MediaSessionCompat.Callback 方法。
4)与媒体会话保持同步
界面应显示媒体会话的当前状态(通过其 PlaybackState 和元数据来描述)。
要在媒体会话的状态或元数据每次发生更改时从媒体会话接收回调,请使用以下两种方法定义
媒体控制器,在客户端中开发者不仅可以使用控制器向Service中的媒体会话发送指令,还可以通过设置MediaControllerCompat.Callback回调方法接收媒体会话的状态,从而根据相应的状态刷新界面UI。MediaController的创建需要媒体会话的配对令牌,因此需在浏览器成功连接服务的回调执行创建的操作。
3. 媒体会话回调
您的媒体会话回调调用多个 API 中的方法来控制播放器,管理音频焦点以及与媒体会话和媒体浏览器服务通信。请注意,响应回调的 MediaSession 逻辑必须保持一致。回调的行为不得依赖于调用方的身份,该调用方可能是运行 MediaSession 的同一应用中的 Activity,也可能是任何其他带有已连接到 MediaSession 的 MediaController 应用中的 Activity。
MediaSessionCompat.Callback callback = new
MediaSessionCompat.Callback() {
@Override
public void onPlay() {}
public void onStop() {}
public void onPause() {}
public void onSkipToNext(){}
public void onSkipToPrevious()
public void onSeekTo(long pos) {}
public void onFastForward(){}
public void onRewind(){}
public void onSetRepeatMode(int repeatMode)
... ...
};
通过设置MediaSessionCompat.Callback回调来接收媒体控制器MediaController发送的指令。当收到指令时会触发Callback中各个指令对应的回调方法(回调方法中会执行播放器相应的操作,如播放、暂停等)。
最后总结一下:
使用的关键类:
- MediaBrowserServiceCompat 媒体浏览器服务
- MediaBrowserCompat 媒体浏览器
- MediaControllerCompat 媒体控制器
- MediaSessionCompat 媒体会话
关键流程梳理:
- MediaBrowserServiceCompat继承自Service,为运行在后台的音频服务;
- MediaBrowserCompat 运行于前台,通过MediaSessionCompat与MediaBrowserServiceCompat建立连接,从而获取到一个MediaControllerCompat用于控制前台音频播放;
- MediaControllerCompat UI界面控制音频播放进度、播放速度、上一曲、下一曲音频播放等等;
- MediaSessionCompat - 运行于后台的MediaBrowserServiceCompat与运行于前台的MediaBrowserCompat通过MediaSessionCompat来建立连接;
- MediaSessionCompat.Callback 用户通过MediaControllerCompat对UI的操作,会通过MediaSessionCompat.Callback 回调到Service端,来操纵“播放器”进行播放、暂停、快进、上一曲、下一曲等操作;
- MediaControllerCompat.Callback Service端播放器播放完成、播放下一曲等操作,通过MediaControllerCompat.Callback回调到UI页面,操纵UI的变化。
官方项目示例:android-MediaBrowserService
一个MediaBrowser开源项目 Android_MediaBrowser_Demo -可以验证上述播放流程
参考文章:
《MediaBrowserCompat MediaBrowserServiceCompat》