MediaSession — Android框架组件,用于管理媒体内容的播放和与外部设备的集成。它提供与蓝牙耳机、耳机、Android Auto和系统媒体中心交互的统一接口。根据Android Developers Guide, 2026,MediaSession取代了过时的RemoteControlClient,并且对于与系统同步的播放器是必需的。
要点
MediaSession — 是Android的系统组件,允许应用声明其媒体活动并从外部接收控制命令。当用户在蓝牙耳机上按下Play按钮时,系统将此事件传递给活动的MediaSession,应用通过其自己的Callback做出响应。
在Android 5.0之前,为此目的使用的是RemoteControlClient,但它没有提供所需的灵活性,也不支持现代场景 — Android Auto、智能手表、智能音箱。MediaSession在API 21中引入,并成为所有具有音频和视频播放功能的Android应用的事实标准。
根据Android Developer Documentation (2026),应用应为每个独立的播放源创建一个MediaSession。一次只能有一个活动会话 — 每个新会话会自动停用前一个会话。
MediaSession与MediaBrowserService的组合提供了完整的媒体管理周期:服务提供内容树(播放列表、目录),而会话接收导航和播放命令。这是Google为音乐播放器、播客和有声读物推荐的架构。
MediaBrowserService作为前景服务运行并带有通知,确保即使在Activity被终止后应用也能继续运行。这对于在应用最小化时必须继续播放的音频播放器至关重要。
会话支持两种类型的交互:它从MediaController(客户端)接收命令,并通过PlaybackState广播状态。MediaController可以在同一进程中,也可以在单独的应用程序中 — 系统通过SessionToken路由请求。
当用户调用Android语音助手并说“播放下一曲”时,系统通过与MediaBrowserService的连接找到活动的MediaSession,并发送ACTION_SKIP_TO_NEXT命令。应用的Callback接收onSkipToNext()调用并更新PlaybackState。
通过setPlaybackState()更新PlaybackState会立即通知所有连接的MediaController。系统媒体中心、蓝牙设备和Android Auto同时接收更新 — 在正常情况下延迟不超过50毫秒。
PlaybackState包含关键状态标志:isPlaying、位置、速度、可用操作(play、pause、seek、stop)。如果没有正确填充PlaybackState,系统不知道应用支持哪些命令,也不会发送相应的事件。
元数据(MediaMetadata)用当前曲目的信息补充状态 — title、artist、album art URI。Android Auto和可穿戴设备使用MediaMetadata在屏幕上显示信息。根据Google I/O 2024,正确填充MediaMetadata可使应用在第三方启动器中的可见性提高40%。
MediaSession的架构由四个相互关联的组件组成,每个组件解决自己的任务。开发者必须实现所有四个组件才能与系统完全集成。
会话在服务或Activity的onCreate中通过调用MediaSessionCompat(context, tag)创建。创建后必须调用setFlags(FLAG_HANDLES_MEDIA_BUTTONS | FLAG_HANDLES_TRANSPORT_CONTROLS)。在onDestroy中调用release()以释放系统资源。
生命周期管理不当是常见错误之一。如果不调用release(),会话会留在系统中,下一个应用可能会收到错误状态。Android 13+在会话泄漏时会在Logcat中显示警告。
基本集成从创建会话和实现Callback开始。建议使用AndroidX media库中的MediaSessionCompat,它为所有Android版本(从API 14到35)提供统一的API。
class MusicService : Service() {
private lateinit var mediaSession: MediaSessionCompat
private lateinit var stateBuilder: PlaybackStateCompat.Builder
override fun onCreate() {
super.onCreate()
mediaSession = MediaSessionCompat(this, "MusicService")
mediaSession.setFlags(
MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
or MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
)
mediaSession.setCallback(MediaSessionCallback())
updatePlaybackState(false)
}
private inner class MediaSessionCallback : MediaSessionCompat.Callback() {
override fun onPlay() {
updatePlaybackState(true)
}
override fun onPause() {
updatePlaybackState(false)
}
}
private fun updatePlaybackState(isPlaying: Boolean) {
stateBuilder = PlaybackStateCompat.Builder()
.setState(
if (isPlaying) PlaybackStateCompat.STATE_PLAYING
else PlaybackStateCompat.STATE_PAUSED,
AudioTrackCompat.CURRENT_POSITION_NOT_SET,
1.0f
)
.setActions(
PlaybackStateCompat.ACTION_PLAY
or PlaybackStateCompat.ACTION_PAUSE
or PlaybackStateCompat.ACTION_SKIP_TO_NEXT
or PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
)
mediaSession.setPlaybackState(stateBuilder.build())
}
override fun onDestroy() {
mediaSession.release()
super.onDestroy()
}
}
在示例中,创建了带有MusicService标记和处理媒体按钮标志的MediaSession。Callback实现onPlay和onPause,更新PlaybackState。setActions方法声明了系统在锁定屏幕和媒体中心显示的可用操作。
private fun setMetadata(title: String, artist: String) {
val metadata = MediaMetadataCompat.Builder()
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, title)
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, artist)
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, 300000L)
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, albumArtUrl)
.build()
mediaSession.setMetadata(metadata)
}
元数据应在每次曲目更改时更新。系统使用METADATA_KEY_TITLE和METADATA_KEY_ARTIST在汽车蓝牙显示屏和可穿戴设备上显示信息。如果未设置封面URI,播放器将显示灰色的占位符。
蓝牙耳机通过AVRCP 1.6+配置文件发送命令。Android将这些命令传输到ACTION_MEDIA_BUTTON意图,当设置了FLAG_HANDLES_MEDIA_BUTTONS标志时,这些意图会被MediaSession拦截。
当用户在蓝牙耳机上按下Play按钮时,系统创建一个带有KEYCODE_MEDIA_PLAY代码的KeyEvent,该事件被分派到Callback的onMediaButtonEvent方法。如果在Callback中实现了onPlay(),系统直接调用它。在耳机上单次按下按钮时,发送KEYCODE_MEDIA_PLAY_PAUSE — 播放器应切换状态。
现代蓝牙耳机可以有最多5个按钮:音量+/-、play/pause、next、previous。每个按钮生成自己的KeyEvent,需要通过Callback正确处理。在许多耳机上,双击play/pause通常被解释为跳转到下一曲(ACTION_SKIP_TO_NEXT)。
根据Android Compatibility Definition Document (2026),所有具有媒体内容的应用都必须正确处理KEYCODE_MEDIA_PLAY_PAUSE。忽略此要求会导致应用在Google Play中音乐和音频类别的评分自动降低。
从Android 11开始,系统媒体中心(Media Control Panel)在通知面板中显示最多5个最近的MediaSession。用户可以在不打开应用的情况下控制播放器。为了正确显示,需要实现MediaBrowserService并正确填充PlaybackState。
Media Control Panel显示:曲目标题、艺术家、封面(来自MediaMetadata)、控制按钮(来自PlaybackState允许的操作)。如果应用在活动播放期间至少每10秒不更新PlaybackState,媒体中心将从面板中隐藏会话。
private fun startForegroundService() {
val notification = NotificationCompat.Builder(this, "media_channel")
.setSmallIcon(R.drawable.ic_play)
.setContentTitle("正在播放")
.setContentText(currentTrackTitle)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setStyle(
androidx.media.app.NotificationCompat.MediaStyle()
.setMediaSession(mediaSession.sessionToken)
)
.build()
startForeground(1001, notification)
}
MediaStyle通知通过sessionToken与MediaSession关联,并显示标准的媒体按钮。如果没有MediaStyle,通知看起来就像没有控制按钮的普通警报。Android 13+需要明确的POST_NOTIFICATIONS权限才能显示。
常见问题
从技术上讲可以,但在一个时刻只有一个会话被视为活动。在不调用setActive(true)的情况下创建新会话时,前一个会话保持活动。建议每个应用或每个独立音频源使用一个会话,并切换活动状态。
MediaSession不会自动管理Audio Focus — 这是单独的机制。在收到onPlay命令时,开发者通过AudioManager自行请求AudioFocus,在失去焦点时通过会话暂停播放。
检查FLAG_HANDLES_MEDIA_BUTTONS和FLAG_HANDLES_TRANSPORT_CONTROLS标志的设置。同时确保会话是活动的(setActive(true))。在Android 12+上,媒体按钮只能通过MediaSession工作 — 旧的registerMediaButtonEventReceiver不受支持。
对于基本的按钮处理 — 不需要。但对于与Android Auto、Wear OS和系统媒体中心的集成,需要MediaBrowserService。Google建议在所有具有长时间音频播放的应用中实现MediaBrowserService。
使用adb shell dumpsys media_session命令查看活动会话、它们的Callback和PlaybackState。该工具将显示所有已注册的会话及其标记、活动和最后已知状态 — 是调试的便捷工具。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。