Media3播放通知下一曲按钮不显示问题求助
解决Media3播放通知不显示下一曲按钮的问题
我基于Media3实现了播放功能,已在SimpleMediaNotificationManager中设置notificationManager.setUseNextAction(true),但播放通知仍未显示下一曲按钮,相关代码如下:
相关代码
SimpleMediaNotificationManager类
private const val NOTIFICATION_ID = 200 private const val NOTIFICATION_CHANNEL_ID = "notification channel id 1" class SimpleMediaNotificationManager(val context: Context, val player: Player) { @RequiresApi(Build.VERSION_CODES.O) fun startNotificationService( mediaSessionService: MediaSessionService, mediaSession: MediaSession ) { buildNotification(mediaSession) startForegroundNotification(mediaSessionService) } private fun buildNotification(mediaSession: MediaSession) { val notificationManager = PlayerNotificationManager.Builder(context, 111, NOTIFICATION_CHANNEL_ID) .setChannelImportance(NotificationUtil.IMPORTANCE_LOW) .setSmallIconResourceId(R.drawable.music) .setChannelDescriptionResourceId(R.string.app_name) .setChannelNameResourceId(R.string.app_name) .setMediaDescriptionAdapter(audioDescriptor) .setNotificationListener(notificationListener) .build() notificationManager.setPriority(NotificationCompat.PRIORITY_MIN) notificationManager.setUseRewindAction(false) notificationManager.setUseFastForwardAction(false) notificationManager.setUsePreviousAction(true) notificationManager.setUsePlayPauseActions(true) notificationManager.setUseNextAction(true) notificationManager.setPlayer(player) } private val notificationListener = object : PlayerNotificationManager.NotificationListener { override fun onNotificationCancelled(notificationId: Int, dismissedByUser: Boolean) { super.onNotificationCancelled(notificationId, dismissedByUser) if (player?.isPlaying!!) { player?.stop() player?.release() } } override fun onNotificationPosted( notificationId: Int, notification: Notification, ongoing: Boolean ) { super.onNotificationPosted(notificationId, notification, ongoing) } } @RequiresApi(Build.VERSION_CODES.O) private fun startForegroundNotification(mediaSessionService: MediaSessionService) { val notification = Notification.Builder(context, NOTIFICATION_CHANNEL_ID) .setCategory(Notification.CATEGORY_SERVICE) .build() mediaSessionService.startForeground(NOTIFICATION_ID, notification) } private val audioDescriptor = object : PlayerNotificationManager.MediaDescriptionAdapter { override fun getCurrentContentTitle(player: Player): CharSequence { return player.currentMediaItem?.mediaMetadata?.albumTitle!! } override fun createCurrentContentIntent(player: Player): PendingIntent? { return pendingIntent() } override fun getCurrentContentText(player: Player): CharSequence? { return "" } override fun getCurrentLargeIcon( player: Player, callback: PlayerNotificationManager.BitmapCallback ): Bitmap? { val bitmapDrawable: BitmapDrawable = ContextCompat.getDrawable( context, R.drawable.cma_logo_render ) as BitmapDrawable return bitmapDrawable.bitmap } } private fun pendingIntent(): PendingIntent? { val intent = Intent(context, MusicPlayerActivity::class.java) return PendingIntent.getActivity( context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) } }
PlayService类(MediaSessionService实现)
class PlayService : MediaSessionService() { private var mediaSession: MediaSession? = null lateinit var notificationManager: SimpleMediaNotificationManager override fun onCreate() { super.onCreate() val player = ExoPlayer.Builder(this).build() notificationManager = SimpleMediaNotificationManager(this, player) mediaSession = MediaSession.Builder(this, player).setSessionActivity(pendingIntent()!!) .setId(Random(5).toString()) .build() } @RequiresApi(Build.VERSION_CODES.O) override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { notificationManager.startNotificationService( mediaSessionService = this, mediaSession = mediaSession!! ) return super.onStartCommand(intent, flags, startId) } override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? = mediaSession override fun onDestroy() { mediaSession?.run { player.release() release() mediaSession = null } super.onDestroy() } private fun pendingIntent(): PendingIntent? { val intent = Intent(applicationContext, MusicPlayerActivity::class.java) return PendingIntent.getActivity( applicationContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) } }
Activity类相关代码
override fun onStart() { super.onStart() val sessionToken = SessionToken(this, ComponentName(this, PlayService::class.java)) val controllerFuture = MediaController.Builder(this, sessionToken).buildAsync() controllerFuture.addListener( { player = controllerFuture.get() player?.seekTo(0, 0) val mediaMetaData = MediaMetadata.Builder() .setAlbumTitle(MusicPlayerData.audioTitle) .build() val mediaItem = MediaItem.Builder().setMediaMetadata(mediaMetaData) .setUri(MusicPlayerData.audioUri) .build() player?.setMediaItem(mediaItem) player?.prepare() player?.play() setPlayerControls() }, MoreExecutors.directExecutor() ) }
问题原因与解决方案
核心原因
Media3的PlayerNotificationManager会根据播放器的hasNext()返回值决定是否显示下一曲按钮。如果播放队列中只有单个媒体项,hasNext()返回false,按钮会被自动隐藏;另外,MediaSession未明确启用下一曲命令、通知渠道优先级过低也可能导致按钮不显示。
解决方案1:添加多个媒体项到播放队列
确保播放器的播放队列中有至少两个媒体项,hasNext()会返回true,按钮会自动显示。修改Activity中的代码:
override fun onStart() { super.onStart() val sessionToken = SessionToken(this, ComponentName(this, PlayService::class.java)) val controllerFuture = MediaController.Builder(this, sessionToken).buildAsync() controllerFuture.addListener( { player = controllerFuture.get() player?.seekTo(0, 0) // 创建多个媒体项组成播放列表 val mediaItems = listOf( MediaItem.Builder() .setMediaMetadata(MediaMetadata.Builder().setAlbumTitle(MusicPlayerData.audioTitle).build()) .setUri(MusicPlayerData.audioUri) .build(), // 添加第二个媒体项 MediaItem.Builder() .setMediaMetadata(MediaMetadata.Builder().setAlbumTitle("第二首").build()) .setUri("第二首音频的URI") .build() ) player?.setMediaItems(mediaItems) player?.prepare() player?.play() setPlayerControls() }, MoreExecutors.directExecutor() ) }
解决方案2:强制显示下一曲按钮(无播放列表时)
如果需要在只有单个媒体项时也显示下一曲按钮(比如自定义跳转逻辑),可以通过自定义ActionProvider强制启用按钮,并确保MediaSession允许下一曲命令:
修改SimpleMediaNotificationManager的buildNotification方法
private fun buildNotification(mediaSession: MediaSession) { val notificationManager = PlayerNotificationManager.Builder(context, 111, NOTIFICATION_CHANNEL_ID) .setChannelImportance(NotificationUtil.IMPORTANCE_DEFAULT) // 提高渠道优先级,避免通知被压缩 .setSmallIconResourceId(R.drawable.music) .setChannelDescriptionResourceId(R.string.app_name) .setChannelNameResourceId(R.string.app_name) .setMediaDescriptionAdapter(audioDescriptor) .setNotificationListener(notificationListener) // 添加自定义下一曲ActionProvider .setCustomActionProviders(listOf( object : PlayerNotificationManager.CustomActionProvider { override fun getCustomAction(player: Player): PlayerNotificationManager.CustomAction? { return PlayerNotificationManager.CustomAction( android.R.drawable.ic_media_next, "下一曲", NotificationCompat.Action.NEXT_MEDIA_ACTION ) } } )) .build() notificationManager.setPriority(NotificationCompat.PRIORITY_DEFAULT) notificationManager.setUseRewindAction(false) notificationManager.setUseFastForwardAction(false) notificationManager.setUsePreviousAction(true) notificationManager.setUsePlayPauseActions(true) notificationManager.setUseNextAction(true) notificationManager.setUseNextActionInCompactView(true) // 确保在紧凑通知中也显示按钮 notificationManager.setPlayer(player) }
修改PlayService的MediaSession配置
override fun onCreate() { super.onCreate() val player = ExoPlayer.Builder(this).build() notificationManager = SimpleMediaNotificationManager(this, player) mediaSession = MediaSession.Builder(this, player) .setSessionActivity(pendingIntent()!!) .setId(Random(5).toString()) // 明确启用下一曲命令 .setAvailableCommands( SessionCommands.Builder() .addAll(Player.DEFAULT_SESSION_COMMANDS) .add(SessionCommand(SessionCommand.COMMAND_CODE_NEXT_MEDIA_ITEM, null)) .build() ) .build() }
解决方案3:检查播放器状态与通知渠道
- 验证
player.hasNext()的返回值,可以添加播放器监听器打印日志:
player?.addListener(object : Player.Listener { override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) { Log.d("PlayerDebug", "hasNext: ${player?.hasNext()}") } })
- 提高通知渠道的优先级,避免通知被系统压缩导致按钮隐藏;
- 确保使用最新版本的Media3库,旧版本可能存在UI显示bug。
内容的提问来源于stack exchange,提问作者primo
相关产品推荐
相关产品推荐

