如何在Wear OS的Jetpack Compose中获取当前播放歌曲及解决权限异常?
在Wear OS Jetpack Compose中获取当前播放歌曲的方案
首先纠正一个误区:你用MediaSessionService是用来实现自己的媒体播放服务(比如你的APP作为音乐播放器),而非获取其他应用的播放状态。要获取系统中当前活跃的媒体会话信息,正确的方式是使用MediaControllerCompat结合系统媒体会话管理。
一、解决你遇到的权限异常(若确需使用MediaSessionService)
如果你的场景是开发自己的媒体播放器,出现java.lang.SecurityException: Missing permission to control media的原因是缺少必要权限,按以下步骤修复:
- 在
AndroidManifest.xml中添加权限与服务声明:<!-- 媒体控制核心权限 --> <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" /> <!-- Android 13+ 读取本地音频文件权限(若需访问本地媒体) --> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <!-- 声明MediaSessionService --> <service android:name=".YourMediaSessionService" android:exported="true"> <intent-filter> <action android:name="android.media.session.MediaSessionService" /> </intent-filter> </service> - 动态申请权限:Android 6.0+ 需动态申请
READ_MEDIA_AUDIO(13+)或READ_EXTERNAL_STORAGE(低版本),MEDIA_CONTENT_CONTROL通常会被系统自动授予给媒体类应用,无需额外动态申请。
二、获取当前播放歌曲的最优实现(Wear OS Jetpack Compose)
使用MediaControllerCompat监听系统活跃媒体会话,是获取当前播放信息的标准方案,步骤如下:
1. 添加依赖
在Module级build.gradle中引入媒体组件依赖:
implementation "androidx.media:media:1.6.0" implementation "androidx.wear:wear-media:1.0.0"
2. Compose中实现监听逻辑
通过MediaSessionManagerCompat监听活跃会话变化,获取媒体元数据与播放状态:
@Composable fun NowPlayingStatus() { val context = LocalContext.current val mediaSessionManager = remember { MediaSessionManagerCompat.getSessionManager(context) } var currentMediaMeta by remember { mutableStateOf<MediaMetadataCompat?>(null) } var playbackState by remember { mutableStateOf<PlaybackStateCompat?>(null) } // 监听活跃媒体会话变更 DisposableEffect(mediaSessionManager) { val sessionCallback = object : MediaSessionManagerCompat.OnActiveSessionsChangedListener { override fun onActiveSessionsChanged(controllers: MutableList<MediaControllerCompat>?) { controllers?.firstOrNull()?.run { currentMediaMeta = metadata playbackState = playbackState // 监听实时播放状态与元数据变化 registerCallback(object : MediaControllerCompat.Callback() { override fun onMetadataChanged(metadata: MediaMetadataCompat?) { currentMediaMeta = metadata } override fun onPlaybackStateChanged(state: PlaybackStateCompat?) { playbackState = state } }) } } } mediaSessionManager.addOnActiveSessionsChangedListener( sessionCallback, ComponentName(context, MediaBrowserServiceCompat::class.java) ) onDispose { mediaSessionManager.removeOnActiveSessionsChangedListener(sessionCallback) } } // 渲染当前播放信息 Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { currentMediaMeta?.let { meta -> Text(text = "当前播放: ${meta.description.title ?: "未知歌曲"}", fontWeight = FontWeight.Bold) Text(text = "歌手: ${meta.description.subtitle ?: "未知歌手"}") playbackState?.let { state -> Text(text = "状态: ${if (state.state == PlaybackStateCompat.STATE_PLAYING) "播放中" else "已暂停"}") } } ?: run { Text(text = "暂无媒体播放") } } }
3. 权限处理
在Compose中动态申请必要权限,确保能读取媒体元数据:
val permissionLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.RequestPermission(), onResult = { isGranted -> if (isGranted) { // 权限授予后可触发会话刷新逻辑 } } ) LaunchedEffect(Unit) { val requiredPermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { Manifest.permission.READ_MEDIA_AUDIO } else { Manifest.permission.READ_EXTERNAL_STORAGE } if (ContextCompat.checkSelfPermission(context, requiredPermission) != PackageManager.PERMISSION_GRANTED) { permissionLauncher.launch(requiredPermission) } }
三、关键注意事项
- 部分第三方音乐APP可能未暴露媒体会话,导致无法获取其播放状态,这是应用自身的限制,无法通过代码解决;
- 测试时需确保有媒体APP处于活跃播放状态,否则
currentMediaMeta会为null; - 若开发自己的播放器,
MediaSessionService需正确初始化MediaSessionCompat并设置回调,确保会话被系统识别。
内容的提问来源于stack exchange,提问作者Matanya Asis
相关产品推荐
相关产品推荐

