目录一、多媒体应用架构1.1音视频传统应用架构1.2MediaSession框架媒体会话媒体控制器二、MediaSession2.1概述2.2MediaBrowser2.2.1MediaB...
目录
一、多媒体应用架构1.1 音视频传统应用架构
1.2 MediaSession 框架
媒体会话
媒体控制器
二、MediaSession
2.1 概述
2.2 MediaBrowser
2.2.1 MediaBrowser.ConnectionCallback
2.2.2 MediaBrowser.ItemCallback
2.2.3 MediaBrowser.MediaItem
2.2.4 MediaBrowser.SubscriptionCallback
2.3 MediaController
2.3.1 MediaController.Callback
2.3.2 MediaController.PlaybackInfo
2.3.3 MediaController.TransportControls
2.4 MediaBrowserService
2.4.1 MediaBrowserService.BrowserRoot
2.5 MediaSession.Callback
2.5.1 MediaSession.QueueItem
2.6 PlaybackState
2.6.1 PlaybackState.Builder
2.6.2 PlaybackState.CustoMACtion
文末
一、多媒体应用架构
1.1 音视频传统应用架构
通常,传统的播放音频或视频的多媒体应用由两部分组成:
播放器:用于吸收数字媒体并将其呈现为视频和/或音频;
界面:带有用于运行播放器并显示播放器状态(可选)的传输控件;

在 android 应用开发中,从零开始构建自己的播放器还可以考虑以下选项:
MediaPlayer :提供准系统播放器的基本功能,支持最常见的音频/视频格式和数据源。ExoPlayer :一个提供低层级 Android 音频 API 的开放源代码库。ExoPlayer 支持 DASH 和 HLS 流等高性能功能,这些功能在 MediaPlayer 中未提供。 众所周知,如果要在应用的后台继续播放音频,最常见的方式就是把 Player 放置在 Service 中,Service 提供一个 Binder 来实现界面与播放器之间的通信。但是,如果遇到锁屏时,如果要与 Service 之间进行通信就不得不用到 AIDL 接口/广播/ContentProvider 来完成与其它应用之间的通信,而这些通信手段既增加了应用开发者之间的沟通成本,也增加了应用之间的耦合度。为了解决上面的问题,Android 官方从 Android5.0 开始提供了 MediaSession 框架。
1.2 MediaSession 框架
MediaSession 框架规范了音视频应用中界面与播放器之间的通信接口,实现界面与播放器之间的完全解耦。MediaSession 框架定义了媒体会话和媒体控制器两个重要的类,它们为构建多媒体播放器应用提供了一个完善的技术架构。
媒体会话和媒体控制器通过以下方式相互通信:使用与标准播放器操作(播放、暂停、停止等)相对应的预定义回调,以及用于定义应用独有的特殊行为的可扩展自定义调用。

媒体会话
媒体会话负责与播放器的所有通信。它会对应用的其他部分隐藏播放器的 API。系统只能从控制播放器的媒体会话中调用播放器。
会话会维护播放器状态(播放/暂停)的表示形式以及播放内容的相关信息。会话可以接收来自一个或多个媒体控制器的 回调 。这样,应用的界面以及运行 Wear OS 和 Android Auto 的配套设备便可以控制您的播放器。响应回调的逻辑必须保持一致。无论哪个客户端应用发起了回调,对 MediaSession 回调的响应都是相同的。
媒体控制器
媒体控制器的作用是隔离界面,界面的代码只与媒体控制器(而非播放器本身)通信,媒体控制器会将传输控制操作转换为对媒体会话的回调。每当会话状态发生变化时,它也会接收来自媒体会话的回调,这为自动更新关联界面提供了一种机制,媒体控制器一次只能连接到一个媒体会话。
当您使用媒体控制器和媒体会话时,就可以在运行时部署不同的接口和/或播放器。这样一来,您可以根据运行应用的设备的功能单独更改该应用的外观和/或性能。
二、MediaSession
2.1 概述
MediaSession 框架主要是用来解决音乐界面和服务之间的通信问题,属于典型的 C/S 架构,有四个常用的成员类,分别是 MediaBrowser、MediaBrowserService、MediaController 和 MediaSession,是整个 MediaSession 框架流程控制的核心。
MediaBrowser:媒体浏览器,用来连接媒体服务 MediaBrowserService 和订阅数据,在注册的回调接口中可以获取到 Service 的连接状态、获取音乐数据,一般在客户端中创建。MediaBrowserService:媒体服务,它有两个关键的回调函数,onGetRoot(控制客户端媒体浏览器的连接请求,返回值中决定是否允许连接),onLoadChildren(媒体浏览器向服务器发送数据订阅请求时会被调用,一般在这里执行异步获取数据的操作,然后在将数据发送回媒体浏览器注册的接口中)。
MediaController:媒体控制器,在客户端中工作,通过控制器向媒体服务器发送指令,然后通过 MediaControllerCompat.Callback 设置回调函数来接受服务端的状态。MediaController 创建时需要受控端的配对令牌,因此需要在浏览器连接成功后才进行 MediaController 的创建。
MediaSession:媒体会话,受控端,通过设置 MediaSessionCompat.Callback 回调来接收 MediaController 发送的指令,收到指令后会触发 Callback 中的回调方法,比如播放暂停等。Session 一般在 Service.onCreate 方法中创建,最后需调用 setSessionToken 方法设置用于和控制器配对的令牌并通知浏览器连接服务成功。 其中,MediaBrowser 和 MediaController 是客户端使用的,MediaBrowserService 和 MediaSession 是服务端使用的。由于客户端和服务端是异步通信,所以采用的大量的回调,因此有大量的回调类,框架示意图如下。

2.2 MediaBrowser
MediaBrowser 是媒体浏览器,用来连接 MediaBrowserService 和订阅数据,通过它的回调接口我们可以获取与 Service的连接状态以及获取在 Service中的音乐库数据。
在客户端(也就是前面提到的界面,或者说是控制端)中创建。媒体浏览器不是线程安全的,所有调用都应在构造 MediaBrowser 的线程上进行。
@RequiresApi(Build.VERSION_CODES.M)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val component = ComponentName(this, MediaService::class.Java)
mMediaBrowser = MediaBrowser(this, component, connectionCallback, null);
mMediaBrowser.connect()
}
2.2.1 MediaBrowser.ConnectionCallback
连接状态回调,当 MediaBrowser 向 service 发起连接请求后,请求结果将在这个 callback 中返回,获取到的 meidaId 对应服务端在 onGetRoot 函数中设置的 mediaId,如果连接成功那么就可以做创建媒体控制器之类的操作了。
@RequiresApi(Build.VERSION_CODES.M)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val component = ComponentName(this, MediaService::class.java)
mMediaBrowser = MediaBrowser(this, component, connectionCallback, null);
mMediaBrowser.connect()
}
private val connectionCallback = object : MediaBrowser.ConnectionCallback() {
override fun onConnected() {
super.onConnected()
... //连接成功后我们才可以创建媒体控制器
}
override fun onConnectionFailed() {
super.onConnectionFailed()
}
override fun onConnectionSuspended() {
super.onConnectionSuspended()
}
}
2.2.2 MediaBrowser.ItemCallback
媒体控制器是负责向 service 发送例如播放暂停之类的指令的,这些指令的执行结果将在这个回调中返回,可重写的函数有很多,比如播放状态的改变,音乐信息的改变等。
private val connectionCallback = object : MediaBrowser.ConnectionCallback() {
override fun onConnected() {
super.onConnected()
... //返回执行结果
if(mMediaBrowser.isConnected) {
val mediaId = mMediaBrowser.root
mMediaBrowser.getItem(mediaId, itemCallback)
}
}
}
@RequiresApi(Build.VERSION_CODES.M)
private val itemCallback = object : MediaBrowser.ItemCallback(){
override fun onItemLoaded(item: MediaBrowser.MediaItem?) {
super.onItemLoaded(item)
}
override fun onError(mediaId: String) {
super.onError(mediaId)
}
}
2.2.3 MediaBrowser.MediaItem
包含有关单个媒体项的信息,用于浏览/搜索媒体。MediaItem依赖于服务端提供,因此框架本身无法保证它包含的值都是正确的。
2.2.4 MediaBrowser.SubscriptionCallback
连接成功后,首先需要的是订阅服务,同样还需要注册订阅回调,订阅成功的话服务端可以返回一个音乐信息的序列,可以在客户端展示获取的音乐列表数据。例如,下面是订阅 MediaBrowserService 中 MediaBrowser.MediaItem 列表变化的回调。
private val connectionCallback = object : MediaBrowser.ConnectionCallback() {
override fun onConnected() {
super.onConnected()
// ...
if(mMediaBrowser.isConnected) {
val mediaId = mMediaBrowser.root
//需要先取消订阅
mMediaBrowser.unsubscribe(mediaId)
//服务端会调用 onLoadChildren
mMediaBrowser.subscribe(mediaId, subscribeCallback)
}
}
}
private val subscribeCallback = object : MediaBrowser.SubscriptionCallback(){
override fun onChildrenLoaded(
parentId: String,
children: MutableList<MediaBrowser.MediaItem>
) {
super.onChildrenLoaded(parentId, children)
}
override fun onChildrenLoaded(
parentId: String,
children: MutableList<MediaBrowser.MediaItem>,
options: Bundle
) {
super.onChildrenLoaded(parentId, children, options)
}
override fun onError(parentId: String) {
super.onError(parentId)
}
override fun onError(parentId: String, options: Bundle) {
super.onError(parentId, options)
}
}
2.3 MediaController
媒体控制器,用来向服务端发送控制指令,例如:播放、暂停等等,在客户端中创建。媒体控制器是线程安全的,MediaController 还有一个关联的权限 android.permission.MEDIA_CONTENT_CONTROL(不是必须加的权限)必须是系统级应用才可以获取,幸运的是车载应用一般都是系统级应用。
同时,MediaController必须在 MediaBrowser 连接成功后才可以创建。 所以,创建 MediaController 的代码如下:
private val connectionCallback = object : MediaBrowser.ConnectionCallback() {
override fun onConnected() {
super.onConnected()
// ...
if(mMediaBrowser.isConnected) {
val sessionToken = mMediaBrowser.sessionToken
mMediaController = MediaController(applicationContext,sessionToken)
}
}
}
2.3.1 MediaController.Callback
用于从 MediaSession 接收回调,所以使用的时候需要将 MediaController.Callback 注册到 MediaSession 中,如下:
private val connectionCallback = object : MediaBrowser.ConnectionCallback() {
override fun onConnected() {
super.onConnected()
// ...
if(mMediaBrowser.isConnected) {
val sessionToken = mMediaBrowser.sessionToken
mMediaController = MediaController(applicationContext,sessionToken)
mMediaController.registerCallback(controllerCallback)
}
}
}
private val controllerCallback = object : MediaController.Callback() {
override fun onAudioInfoChanged(info: MediaController.PlaybackInfo?) {
super.onAudioInfoChanged(info)
... //回调方法接收受控端的状态,从而根据相应的状态刷新界面 UI
}
override fun onExtrasChanged(extras: Bundle?) {
super.onExtrasChanged(extras)
}
// ...
}
2.3.2 MediaController.PlaybackInfo
获取当前播放的音频信息,包含播放的进度、时长等。
2android.3.3 MediaController.TransportControls
用于控制会话中媒体播放的接口。客户端可以通过 Session 发送媒体控制命令,使用方式如下:
private val connectionCallback = object : MediaBrowser.ConnectionCallback() {
override fun onConnected() {
super.onConnected()
// ...
if(mMediaBrowser.isConnected) {
val sessionToken = mMediaBrowser.sessionToken
mMediaController = MediaController(applicationContext,sessionToken)
// 播放媒体
mMediaController.transportControls.play()
// 暂停媒体
mMediaController.transportControls.pause()
}
}
}
2.4 MediaBrowserService
媒体浏览器服务,继承自 Service,MediaBrowserService 属于服务端,也是承载播放器(如 MediaPlayer、ExoPlayer 等)和 MediaSession 的容器。 继承 MediaBrowserService js后,我们需要复写 onGetRoot和 onLoadChildren两个方法。onGetRoot 通过的返回值决定是否允许客户端的 MediaBrowser 连接到 MediaBrowserService。 当客户端调用 MediaBrowser.subscribe时会触发 onLoadChildren 方法。下面是使用事例:
const val FOLDERS_ID = "__FOLDERS__"
const val ARTISTS_ID = "__ARTISTS__"
const val ALBUMS_ID = "__ALBUMS__"
const val GENRES_ID = "__GENRES__"
const val ROOT_ID = "__ROOT__"
class MediaService : MediaBrowserService() {
override fun onGetRoot(
clientPackageName: String,
clientUid: Int,
rootHints: Bundle?
): BrowserRoot? {
// 由 MediaBrowser.connect 触发,可以通过返回 null 拒绝客户端的连接。
return BrowserRoot(ROOT_ID, null)
}
override fun onLoadChildren(
parentId: String,
result: Result<MutableList<MediaBrowser.MediaItem>>
) {
//由 MediaBrowser.subscribe 触发
when (parentId) {
ROOT_ID -> {
// 查询本地媒体库
result.detach()
result.sendResult()
}
FOLDERS_ID -> {
}
ALBUMS_ID -> {
}
ARTISTS_ID -> {
}
GENRES_ID -> {
}
else -> {
}
}
}
}
最后,还需要在 manifest 中注册这个 Service。
<service
android:name=".MediaService"
android:label="@string/service_name">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
2.4.1 MediaBrowserService.BrowserRoot
返回包含浏览器服务首次连接时需要发送给客户端的信息。构造函数如下:
MediaBrowserService.BrowserRoot(String rootId, Bundle extras)
除此之外,还有两个方法:
getExtras():获取有关浏览器服务的附加信息getRootId():获取用于浏览的根 ID 2.4.2 MediaBrowserService.Result
包含浏览器服务返回给客户端的结果集。通过调用 sendResult()将结果返回给调用方,但是在此之前需要调用 detach()。
detach():将此消息与当前线程分离,并允许稍后进行调用 sendResult(T)sendResult():将结果发送回调用方。 2.5 MediaSession
媒体会话,即**受控端。**通过设定 MediaSession.Callback回调来接收媒体控制器 MediaController发送的指令,如控制音乐的【上一曲】、【下一曲】等。
创建 MediaSession后还需要调用 setSessionToken()方法设置用于和**控制器配对的令牌,使用方式如下:
const val FOLDERS_ID = "__FOLDERS__"
const val ARTISTS_ID = "__ARTISTS__"
const val ALBUMS_ID = "__ALBUMS__"
const val GENRES_ID = "__GENRES__"
const val ROOT_ID = "__ROOT__"
class MediaService : MediaBrowserService() {
private lateinit var mediaSession: MediaSession;
override fun onCreate() {
super.onCreate()
mediaSession = MediaSession(this, "TAG")
mediaSession.setCallback(callback)
sessionToken = mediaSession.sessionToken
}
// 与 MediaController.transportControls 中的大部分方法都是一一对应的
// 在该方法中实现对 播放器 的控制,
private val callback = object : MediaSession.Callback() {
override fun onPlay() {
super.onPlay()
// 处理 播放器 的播放逻辑。
// 车载应用的话,别忘了处理音频焦点
}
override fun onPause() {
super.onPause()
}
}
override fun onGetRoot(
clientPackageName: String,
clientUid: Int,
rootHints: Bundle?
): BrowserRoot? {
Log.e("TAG", "onGetRoot: $rootHints")
return BrowserRoot(ROOT_ID, null)
}
override fun onLoadChildren(
parentId: String,
result: Result<MutableList<MediaBrowser.MediaItem>>
) {
result.detach()
when (parentId) {
ROOT_ID -> {
result.sendResult(null)
}
FOLDERS_ID -> {
}
ALBUMS_ID -> {
}
ARTISTS_ID -> {
}
GENRES_ID -> {
}
else -> {
}
}
}
override fun onLoadItem(itemId: String?, result: Result<MediaBrowser.MediaItem>?) {
super.onLoadItem(itemId, result)
Log.e("TAG", "onLoadItem: $itemId")
}
}
2.5 MediaSession.Callback
接收来自客户端或系统的媒体按钮、传输控件和命令,入【上一曲】、【下一曲】。与 MediaController.transportControls 中的大部分方法都是一一对应的。
private val callback = object : MediaSession.Callback() {
override fun onPlay() {
super.onPlay()
if (!mediaSession.isActive) {
mediaSession.isActive = true
}
//更新播放状态.
val state = PlaybackState.Builder()
.setState(
PlaybackState.STATE_PLAYING,1,1f
)
.build()
mediaSession.setPlaybackState(state)
}
override fun onPause() {
super.onPause()
}
override fun onStop() {
super.onStop()
}
}
2.5.1 MediaSession.QueueItem
播放队列一部分的单个项目,相比 MediaMetadata,多了一个 ID 属性。常用的方法有:
getDescription():返回介质的说明,包含媒体的基础信息,如标题、封面等。getQueueId():获取此项目的队列 ID。
MediaSession.Token
表示正在进行的会话,可以通过会话所有者传递给客户端,以允许客户端与服务端之间建立通信。
2.6 PlaybackState
用于承载播放状态的类。如当前播放位置和当前控制功能。在 MediaSession.Callback更改状态后需要调用 MediaSession.setPlaybackState把状态同步给客户端。
private val callback = object : MediaSession.Callback() {
override fun onPlay() {
super.onPlay()
// 更新状态
val state = PlaybackState.Builder()
.setState(
PlaybackState.STATE_PLAYING,1,1f
)
.build()
mediaSession.setPlaybackState(state)
}
}
2.6.1 PlaybackState.Builder
PlaybackState.Builder 主要用来创建 PlaybackState 对象,创建它使用的是建造者模式,如下。
PlaybackState state = new PlaybackState.Builder()
.setState(PlaybackState.STATE_PLAYING,
mMediaPlayer.getCurrentPosition(), PLAYBACK_SPEED)
.setActions(PLAYING_ACTIONS)
.addCustomAction(mShuffle)
.setActiveQueueItemId(mQueue.get(mCurrentQueueIdx).getQueueId())
.build();
2.6.2 PlaybackState.CustomAction
CustomActions可用于通过将特定于应用程序的操作发送给 MediaControllers,这样就可以扩展标准传输控件的功能。
CustomAction action = new CustomAction
.Builder("android.car.media.localmediaplayer.shuffle",
mContext.getString(R.string.shuffle),
R.drawable.shuffle)
.build();
PlaybackState state = new PlaybackState.Builder()
.setState(PlaybackState.STATE_PLAYING,
mMediaPlayer.getCurrentPosition(), PLAYBACK_SPEED)
.setActions(PLAYING_ACTIONS)
.addCustomAction(action)
.setActiveQueueItemId(mQueue.get(mCurrentQueueIdx).getQueueId())
.build();
常见的 API,有如下一些:
getAction():返回 CustomAction 的 actiongetExtras():返回附加项,这些附加项提供有关操作的其他特定于应用程序的信息
getIcon():返回 package 中图标的资源 ID
getName():返回此操作的显示名称 2.7 MediaMetadata
包含有关项目的基础数据,例如标题、艺术家等。一般需要服务端从本地数据库或远端查询出原始数据在封装成 MediaMetadata 再通过 MediaSession.setMetadata(metadata)返回到客户端的 MediaController.Callback.onMetadataChanged中。
常见的 API 有如下:
containsKey():如果给定的 key 包含在元数据中,则返回 true。describeContents():描述此可打包实例的封送处理表示中包含的特殊对象的种类。
getBitmap():返回给定的 key 的 Bitmap,如果给定 key 不存在位图,则返回 null。
getBitmapDimensionLimit():获取创建此元数据时位图的宽度/高度限制
getDescription():获取此元数据的简单说明以进行显示。
keySet():返回一个 Set,其中包含在此元数据中用作 key 的字符串。 三、示例
下图是 MediaSession 框架核心类的通信过程。

可以看到,在 MediaSession 框架中,首先客户端通过 MediaBrowserService 连接到 MediaBrowserService,MediaBrowserService 接受到请求之后处理相关的请求,MediaSession 控制播放状态,并将状态同步给客户端,客户端最后 MediaController 进行相应的操作。
客户端示例代码:
class MainActivity : AppCompatActivity() {
private lateinit var mMediaBrowser: MediaBrowser
private lateinit var mMediaController: MediaController
@RequiresApi(Build.VERSION_CODES.M)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val component = ComponentName(this, MediaService::class.java)
mMediaBrowser = MediaBrowser(this, component, connectionCallback, null);
// 连接到 MediaBrowserService,会触发 MediaBrowserService 的 onGetRoot 方法。
mMediaBrowser.connect()
findViewById<Button>(R.id.btn_play).setOnClickListener {
mMediaController.transportControls.play()
}
}
private val connectionCallback = object : MediaBrowser.ConnectionCallback() {
override fun onConnected() {
super.onConnected()
if (mMediaBrowser.isConnected) {
val sessionToken = mMediaBrowser.sessionToken
mMediaController = MediaController(applicationContext, sessionToken)
mMediaController.registerCallback(controllerCallback)
// 获取根 mediaId
val rootMediaId = mMediaBrowser.root
// 获取根 mediaId 的 item 列表,会触发 MediaBrowserService.onLoadItem 方法
mMediaBrowser.getItem(rootMediaId,itemCallback)
mMediaBrowser.unsubscribe(rootMediaId)
// 订阅服务端 media item 的改变,会触发 MediaBrowserService.onLoadChildren 方法
mMediaBrowser.subscribe(rootMediaId, subscribeCallback)
}
}
}
privatepython val controllerCallback = object : MediaController.Callback() {
override fun onPlaybackStateChanged(state: PlaybackState?) {
super.onPlaybackStateChanged(state)
Log.d("TAG", "onPlaybackStateChanged: $state")
when(state?.state){
PlaybackState.STATE_PLAYING ->{
// 处理 UI
}
PlaybackState.STATE_PAUSED ->{
// 处理 UI
}
// 还有其它状态需要处理
}
}
//音频信息,音量
override fun onAudioInfoChanged(info: MediaController.PlaybackInfo?) {
super.onAudioInfoChanged(info)
val currentVolume = info?.currentVolume
// 显示在 UI 上
}
override fun onMetadataChanged(metadata: MediaMetadata?) {
super.onMetadataChanged(metadata)
val artUri = metadata?.getString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI)
// 显示 UI 上
}
override fun ohttp://www.cppcns.comnSessionEvent(event: String, extras: Bundle?) {
super.onSessionEvent(event, extras)
Log.d("TAG", "onSessionEvent: $event")
}
// ...
}
private val subscribeCallback = object : MediaBrowser.SubscriptionCallback() {
override fun onChildrenLoaded(
parentId: String,
children: MutableList<MediaBrowser.MediaItem>
) {
super.onChildrenLoaded(parentId, children)
}
override fun onChildrenLoaded(
parentId: String,
children: MutableList<MediaBrowser.MediaItem>,
options: Bundle
) {
super.onChildrenLoaded(parentId, children, options)
}
override fun onError(parentId: String) {
super.onError(parentId)
}
}
private val itemCallback = object : MediaBrowser.ItemCallback() {
override fun onItemLoaded(item: MediaBrowser.MediaItem?) {
super.onItemLoaded(item)
}
override fun onError(mediaId: String) {
super.onError(mediaId)
}
}
}
下面是服务端的示例源码,主要用于处理客户端的请求,并将结果返回给客户端。
const val FOLDERS_ID = "__FOLDERS__"
const val ARTISTS_ID = "__ARTISTS__"
const val ALBUMS_ID = "__ALBUMS__"
const val GENRES_ID = "__GENRES__"
const val ROOT_ID = "__ROOT__"
class MediaService : MediaBrowserService() {
// 控制是否允许客户端连接,并返回 root media id 给客户端
override fun onGetRoot(
clientPackageName: String,
clientUid: Int,
rootHints: Bundle?
): BrowserRoot? {
Log.e("TAG", "onGetRoot: $rootHints")
return BrowserRoot(ROOT_ID, null)
}
// 处理客户端的订阅信息
override fun onLoadChildren(
parentId: String,
result: Result<MutableList&uqPnXlt;MediaBrowser.MediaItem>>
) {
Log.e("TAG", "onLoadChildren: $parentId")
result.detach()
when (parentId) {
ROOT_ID -> {
result.sendResult(null)
}
FOLDERS_ID -> {
}
ALBUMS_ID -> {
}
ARTISTS_ID -> {
}
GENRES_ID -> {
}
else -> {
}
}
}
override fun onLoadItem(itemId: String?, result: Result<MediaBrowser.MediaItem>?) {
super.onLoadItem(itemId, result)
Log.e("TAG", "onLoadItem: $itemId")
// 根据 itemId,返回对用 MediaItem
result?.detach()
result?.sendResult(null)
}
private lateinit var mediaSession: MediaSession;
override fun onCreate() {
super.onCreate()
mediaSession = MediaSession(this, "TAG")
mediaSession.setCallback(callback)
// 设置 token
sessionToken = mediaSession.sessionToken
}
// 与 MediaController.transportControls 中的方法是一一对应的。
// 在该方法中实现对 播放器 的控制,
private val callback = object : MediaSession.Callback() {
override fun onPlay() {
super.onPlay()
// 处理 播放器 的播放逻辑。
// 车载应用的话,别忘了处理音频焦点
Log.e("TAG", "onPlay:")
if (!mediaSession.isActive) {
mediaSession.isActive = true
}
// 更新状态
val state = PlaybackState.Builder()
.setState(
PlaybackState.STATE_PLAYING, 1, 1f
)
.build()
mediaSession.setPlaybackState(state)
}
override fun onPause() {
super.onPause()
}
override fun onStop() {
super.onStop()
}
//省略其他方法
}
}
上文主要介绍车载自媒体开发与MediaSession框架解析;这只是其中一小部分;更多的Android车载技术可以前往[私信]领取,里面内容如下脑图所示:(需要可以参考,当做辅导资料)

文末
车载系统开发,在这几年岗位逐渐增多。Android转岗车载开发是个很好的发展方向。汽车的普及同比10年增长300%以上;近几年的新能源汽车逐渐普及,车载开发人员更是需求很大;普遍缺少人才。
感觉Android市场下滑的厉害,淘汰人员逐渐增多。一定要眼看未来,才没有近忧。
以上就是Android车载多媒体开发MediaSession框架示例详解的详细内容,更多关于Android车载多媒体MediaSession的资料请关注我们其它相关文章!










