You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android调用startForeground报Bad notification异常解决方法

问题描述

开发音乐播放器应用实现播放通知前台服务时,调用startForeground()方法抛出如下致命异常:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.aashushaikh.musicplayer, PID: 19998
    android.app.RemoteServiceException: Bad notification for startForeground
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2005)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

已尝试过相关公开解决方案,均未生效。


已提供的相关代码

ApplicationClass.kt

class ApplicationClass: Application() {

    companion object{
        const val CHANNEL_ID = "channel1"
        const val PLAY = "play"
        const val NEXT = "next"
        const val PREVIOUS = "previous"
        const val EXIT = "exit"
        const val NOTIFICATION_ID = 0
    }

    override fun onCreate() {
        super.onCreate()
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
            //Creating the channel for the notification
            val notificationChannel = NotificationChannel(CHANNEL_ID, "Now Playing Song", NotificationManager.IMPORTANCE_HIGH)
            notificationChannel.description = "This is an Important Channel for showing the songs"
            notificationChannel.lockscreenVisibility = Notification.VISIBILITY_SECRET

            //Manager is necessary to create the notification channel
            val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            notificationManager.createNotificationChannel(notificationChannel)
        }
    }
}

MusicService.kt(通知构建片段)

val notification = NotificationCompat.Builder(baseContext, ApplicationClass.CHANNEL_ID)
    .setContentTitle(PlayerActivity.musicListPlayerActivity[PlayerActivity.songPosition].title)
    .setContentText(PlayerActivity.musicListPlayerActivity[PlayerActivity.songPosition].artist)
    .setSmallIcon(R.drawable.ic_playlist)
    .setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.splash_screen))
    .setStyle(androidx.media.app.NotificationCompat.MediaStyle().setMediaSession(mediaSession.sessionToken))
    .setPriority(NotificationCompat.PRIORITY_LOW)
    .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
    .setOnlyAlertOnce(true)
    .addAction(R.drawable.ic_previous, "Previous", null)
    .addAction(R.drawable.ic_pause, "Pause", null)
    .addAction(R.drawable.ic_next, "Next", null)
    .addAction(R.drawable.ic_exit, "Exit", null)
    .build()

startForeground(1, notification)

PlayerActivity.kt

在onServiceConnected回调中调用上述showNotification()方法,完整代码如下:

class PlayerActivity : AppCompatActivity(), ServiceConnection {

    private lateinit var binding: ActivityPlayerBinding

    companion object{
        lateinit var musicListPlayerActivity: ArrayList<Music>
        var songPosition: Int = 0
        var isPlaying: Boolean = false
        var musicService: MusicService? = null
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setTheme(R.style.coolPink)
        binding = ActivityPlayerBinding.inflate(layoutInflater)
        setContentView(binding.root)

        //Starting the service
        val intent = Intent(this, MusicService::class.java)
        bindService(intent, this, BIND_AUTO_CREATE)
        startService(intent)

        initializeLayout()

        binding.playPauseBtnPA.setOnClickListener {
            if(isPlaying == true){
                pauseMusic()
            }else{
                playMusic()
            }
        }
        binding.previousBtnPA.setOnClickListener {
            prevNextSong(false)
        }
        binding.nextBtnPA.setOnClickListener {
            prevNextSong(true)
        }
    }

    private fun setLayout(){
        Glide.with(this).load(musicListPlayerActivity[songPosition].artUri)
            .apply (RequestOptions.placeholderOf(R.drawable.ic_noimage)) .into(binding.imagePlayer)

        binding.songNamePlayer.text = musicListPlayerActivity[songPosition].title
    }

    private fun createMediaPlayer(){
        try {
            musicService?.mediaPlayer?.let {
                musicService!!.mediaPlayer!!.reset()
                musicService!!.mediaPlayer!!.setDataSource(musicListPlayerActivity[songPosition].path)
                musicService!!.mediaPlayer!!.prepare()
                musicService!!.mediaPlayer!!.start()
                isPlaying = true
            } ?: kotlin.run {
                musicService!!.mediaPlayer = MediaPlayer()
            }
        } catch (e: Exception) {
            return
        }
    }

    private fun initializeLayout(){
        songPosition = intent.getIntExtra("index", 0)

        when(intent.getStringExtra("class")){
            "MusicAdapter" -> {
                musicListPlayerActivity = ArrayList()
                musicListPlayerActivity.addAll(MainActivity.musicListMA)
                setLayout()
            }
            "MainActivity" -> {
                musicListPlayerActivity = ArrayList()
                musicListPlayerActivity.addAll(MainActivity.musicListMA)
                musicListPlayerActivity.shuffle()
                setLayout()
            }
        }
    }

    private fun playMusic(){
        binding.playPauseBtnPA.setIconResource(R.drawable.ic_pause)
        isPlaying = true
        musicService!!.mediaPlayer!!.start()
    }
    private fun pauseMusic(){
        binding.playPauseBtnPA.setIconResource(R.drawable.ic_play)
        isPlaying = false
        musicService!!.mediaPlayer!!.pause()
    }

    private fun prevNextSong(increment: Boolean){
        if(increment){
            setSongPosition(true)
            setLayout()
            createMediaPlayer()
        }else{
            setSongPosition(false)
            setLayout()
            createMediaPlayer()
        }
    }
    private fun setSongPosition(increment: Boolean){
        if(increment){
            if(musicListPlayerActivity.size - 1 == songPosition){
                songPosition = 0
            }else{
                ++songPosition
            }
        }else{
            if(songPosition == 0){
                songPosition = musicListPlayerActivity.size - 1
            }else{
                --songPosition
            }
        }
    }

    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        var binder = service as MusicService.MyBinder
        musicService = binder.currentService()
        createMediaPlayer()
        musicService!!.showNotification()
    }

    override fun onServiceDisconnected(p0: ComponentName?) {
        musicService = null
    }
}

修复方案

该报错由通知配置与前台服务系统规则不匹配触发,按以下点逐一修改即可解决:

  • 修正通知渠道与通知本身的配置冲突:你在ApplicationClass中给通知渠道设置了lockscreenVisibility = Notification.VISIBILITY_SECRET,但构建通知时又设置了setVisibility(NotificationCompat.VISIBILITY_PUBLIC),参数冲突会直接触发系统通知校验失败,将渠道的可见性改为Notification.VISIBILITY_PUBLIC即可,音乐播放常驻通知不需要设置为SECRET级别。
  • 对齐通知渠道与通知的优先级:你给渠道设置了IMPORTANCE_HIGH(会触发弹窗、提示音),但通知本身设置的是PRIORITY_LOW,优先级不匹配也会被系统拦截,将渠道重要度改为NotificationManager.IMPORTANCE_LOW即可,和通知优先级对齐。
  • 禁止使用0作为前台服务通知ID:Android 12及以上版本对前台服务通知ID为0的情况会直接抛出Bad notification异常,你虽然当前调用startForeground传了ID=1,但建议统一使用你定义的非0常量值作为通知ID,避免后续修改出错。
  • 确认mediaSession初始化时机:你在MediaStyle中传入了mediaSession.sessionToken,要确保调用showNotification()时mediaSession已经完成初始化、不为空,否则媒体样式通知构建失败也会触发该异常。
  • 补充通知权限适配:如果targetSdkVersion >= 33,需要在AndroidManifest.xml中添加<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />,并在应用启动时动态申请通知权限,Android 13及以上版本中,未获得通知权限时调用startForeground()会直接抛出该异常。
  • 检查通知小图标规范:确保R.drawable.ic_playlist是符合Android通知规范的小图标(纯透明背景、白色图案的矢量/位图资源),不要使用带彩色填充背景的图片作为小图标,否则在部分厂商ROM上会判定为无效通知。

修改后的通知渠道创建代码参考:

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
    val notificationChannel = NotificationChannel(CHANNEL_ID, "Now Playing Song", NotificationManager.IMPORTANCE_LOW)
    notificationChannel.description = "Channel for playing song notification"
    notificationChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
    notificationChannel.setShowBadge(false)
    val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    notificationManager.createNotificationChannel(notificationChannel)
}

另外不要在onServiceConnected中立刻调用showNotification(),最好把通知构建、startForeground的调用逻辑放到MusicService自身的onCreate或onStartCommand生命周期中,避免服务未完全启动就调用前台服务方法触发异常。


内容的提问来源于stack exchange,提问作者Aashu Shaikh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 12:09:48