Android Media3 MediaSessionService未生成媒体通知问题求助
问题描述
根据Android官方文档说明:
MediaSessionService会以MediaNotification的形式自动为您创建MediaStyle通知。
我实现了一个支持“播放”歌曲和获取元数据的最小化MediaSessionService,可通过Activity中的MediaController进行控制,但在应用外无法看到用于控制的通知。
补充说明:应用已获取android.permission.POST_NOTIFICATIONS权限,且未触发onForegroundServiceStartNotAllowedException,但仍无通知显示。
相关代码
PlaybackService代码
@OptIn(UnstableApi::class) class PlaybackService : MediaSessionService() { private val _logger = Logger.getLogger("PlaybackService") private val _job = SupervisorJob() private var _mediaSession: MediaSession? = null override fun onCreate() { super.onCreate() val scope = CoroutineScope(Dispatchers.IO + _job) _mediaSession = MediaSession.Builder(this, Player(mainLooper, scope)).build() setListener(object : Listener { override fun onForegroundServiceStartNotAllowedException() { _logger.warning("Foreground service start not allowed.") } }) } override fun onDestroy() { _mediaSession?.player?.release(); _mediaSession?.release(); _job.cancel() super.onDestroy() } override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? { return _mediaSession } private class Player(looper: Looper, val scope: CoroutineScope) : SimpleBasePlayer(looper) { private val _logger = Logger.getLogger("PlaybackService") private val _availableCommands: Commands = Commands.Builder().addAll( COMMAND_PLAY_PAUSE, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_GET_CURRENT_MEDIA_ITEM, COMMAND_GET_METADATA ).build(); private val _metadata = MediaMetadata.Builder() .setArtist("Artist") .setTitle("Title") .build() private val _audioAttributes = AudioAttributes.Builder() .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC) .setUsage(C.USAGE_MEDIA) .build() override fun getState(): State { return State.Builder() .setAvailableCommands(_availableCommands) .setPlaylist(listOf(MediaItemData.Builder("") .setMediaMetadata(_metadata) .build())) .setAudioAttributes(_audioAttributes) .build() } override fun handleSetPlayWhenReady(playWhenReady: Boolean) = scope.future { onPlayPause(playWhenReady) } private suspend fun onPlayPause(play: Boolean) { delay(500) if (play) _logger.info("Play command invoked.") else _logger.info("Pause command invoked.") } } }
MediaController使用代码
override fun onStart() { super.onStart() val context = requireContext() if (ContextCompat.checkSelfPermission(context, "android.permission.POST_NOTIFICATIONS") != PERMISSION_GRANTED) { _logger.info("Requesting permission for 'android.permission.POST_NOTIFICATIONS'...") ActivityCompat.requestPermissions(requireActivity(), arrayOf("android.permission.POST_NOTIFICATIONS"), 0) } else _logger.info("Already has permission for 'android.permission.POST_NOTIFICATIONS'.") val sessionToken = SessionToken(context, ComponentName(context, PlaybackService::class.java)) val future = MediaController.Builder(context, sessionToken).buildAsync(); future.addListener({ val mediaController = future.get() mediaController.play() val metadata = mediaController.mediaMetadata }, MoreExecutors.directExecutor()) }
Android清单文件
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.LayoutPreview" tools:targetApi="31"> <service android:name=".PlaybackService" android:foregroundServiceType="mediaPlayback" android:exported="true"> <intent-filter> <action android:name="androidx.media3.session.MediaSessionService"/> <action android:name="android.intent.action.MEDIA_BUTTON" /> </intent-filter> </service> <receiver android:name="androidx.media.session.MediaButtonReceiver" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MEDIA_BUTTON" /> </intent-filter> </receiver> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
问题修复方案
你遗漏了两个核心逻辑,导致自动通知未触发:
1. 播放器状态未完善关键字段
MediaSessionService需要明确的播放状态、进度信息才会触发通知逻辑。修改Player类:
private class Player(looper: Looper, val scope: CoroutineScope) : SimpleBasePlayer(looper) { private val _logger = Logger.getLogger("PlaybackService") private var _isPlaying = false // 新增播放状态标记 // ... 原有代码不变 ... override fun getState(): State { return State.Builder() .setAvailableCommands(_availableCommands) .setPlaylist(listOf(MediaItemData.Builder("") .setMediaMetadata(_metadata) .build())) .setAudioAttributes(_audioAttributes) // 补充播放状态标记 .setPlayWhenReady(_isPlaying) // 补充播放状态类型 .setPlaybackState(if (_isPlaying) PLAYBACK_STATE_PLAYING else PLAYBACK_STATE_PAUSED) // 补充播放进度(必须字段,否则通知逻辑不会触发) .setPositionMs(0) .setDurationMs(300000) // 示例:设置歌曲时长为5分钟 .build() } private suspend fun onPlayPause(play: Boolean) { delay(500) _isPlaying = play notifyStateChanged() // 通知状态更新 if (play) _logger.info("Play command invoked.") else _logger.info("Pause command invoked.") } }
2. 绑定会话活动并监听状态变化
在PlaybackService的onCreate中,给MediaSession绑定会话活动,并监听播放器状态变化:
override fun onCreate() { super.onCreate() val scope = CoroutineScope(Dispatchers.IO + _job) val player = Player(mainLooper, scope) _mediaSession = MediaSession.Builder(this, player).build() // 绑定会话活动,通知需要关联的Activity _mediaSession?.setSessionActivity(PendingIntent.getActivity( this@PlaybackService, 0, Intent(this@PlaybackService, MainActivity::class.java), PendingIntent.FLAG_IMMUTABLE )) // 监听播放器状态变化,确保服务能感知播放状态 player.addListener(object : Player.Listener { override fun onStateChanged(state: State) { // 状态变化时自动触发前台通知逻辑 } }) setListener(object : Listener { override fun onForegroundServiceStartNotAllowedException() { _logger.warning("Foreground service start not allowed.") } }) }
额外检查项
- 确保使用最新版本的Media3依赖,旧版本可能存在自动通知逻辑的bug
- 测试时确认播放器确实进入
PLAYING状态,只有播放中才会触发前台通知
内容的提问来源于stack exchange,提问作者Dark Daskin
相关产品推荐
相关产品推荐

