Android RemoteServiceException: Context.startForegroundService() 没有调用 Service.startForeground()

Android RemoteServiceExeption: Context.startForegroundService() did not then call Service.startForeground()

我知道,关于这个错误已经有很多问题了。但是我尝试了很多解决方案,但对我没有任何帮助。我的应用程序中有一个带有计时器的通知服务,crashlytics 报告了数百次崩溃(android 8 - 12),如下所示:

ForegroundServiceDidNotStartInTimeException:

Fatal Exception: android.app.ForegroundServiceDidNotStartInTimeException
Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{cac9d0b u0 com.malmatech.eggtimer/.NotificationUtil}

android.app.ActivityThread.throwRemoteServiceException (ActivityThread.java:2134)
android.app.ActivityThread.access00 (ActivityThread.java:309)
android.app.ActivityThread$H.handleMessage (ActivityThread.java:2363)
android.os.Handler.dispatchMessage (Handler.java:106)
android.os.Looper.loopOnce (Looper.java:226)
android.os.Looper.loop (Looper.java:313)
android.app.ActivityThread.main (ActivityThread.java:8582)
java.lang.reflect.Method.invoke (Method.java)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:563)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1133)

和 RemoteServiceException:

Fatal Exception: android.app.RemoteServiceException
Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{d760a21 u0 com.malmatech.eggtimer/.NotificationUtil}

android.app.ActivityThread$H.handleMessage (ActivityThread.java:2313)
android.os.Handler.dispatchMessage (Handler.java:107)
android.os.Looper.loop (Looper.java:213)
android.app.ActivityThread.main (ActivityThread.java:8178)
java.lang.reflect.Method.invoke (Method.java)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:513)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1101)

我仔细检查了我的代码并搜索了网络,但没有找到任何东西,也从未在我自己的设备或模拟器上遇到这些异常...


通知仅在计时器为 运行 且应用程序关闭时显示。在我的 TimerActivity 的 onPause() 中,我启动服务:

if (timerState == TimerState.Running){
    timer.cancel()
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        MyContext.getContext().startForegroundService(Intent(this, NotificationUtil::class.java))
    } else {
        startService(Intent(this, NotificationUtil::class.java))
    }
}

在 onResume() 中我停止了服务:

try {
    MyContext.getContext().stopService(
        Intent(
            MyContext.getContext(),
            NotificationUtil::class.java
        )
    )
} catch (ex: Exception) {
    ex.printStackTrace()
}

这是我的服务 class,我删除了一些不相关的代码,但应该包括管理通知的所有内容。

class NotificationUtil : Service() {
override fun onBind(intent: Intent?): IBinder? {
    return null
}

var eggCount = 1
private var secondsRemaining: Long = 0
private lateinit var timer: CountDownTimer
// some more, irrelevant for question

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    showNotification()
    log("onStartCommand executed with startId: $startId")
    if (intent != null) {
        val action = intent.action
        log("using an intent with action $action")
    } else {
        log("with a null intent. It has been probably restarted by the system.")
    }
    // by returning this we make sure the service is restarted if the system kills the service
    return START_STICKY
}

override fun onCreate() {
    super.onCreate()

    //irrelevant Code - getting an array from SharedPreferences and setting up textToSpeech

    showNotification()
    startTimer()
}

override fun onDestroy() {
    super.onDestroy()
    hideNotification()
    timer.cancel()
}

fun showNotification(done: Boolean = false) {
    val nBuilder = NotificationCompat.Builder(applicationContext, "menu_timer")
        .setSmallIcon(R.drawable.ic_notification_res_specific)
        .setOngoing(true)
        .setAutoCancel(true)
        .setDefaults(0)
        .setContentIntent(
            getPendingIntentWithStack(
                applicationContext,
                TimerActivity::class.java
            )
        )
        .setPriority(PRIORITY_MAX)
        .setVisibility(VISIBILITY_PUBLIC)
        .setNotificationSilent()

    if (!done) {

        //irrelecantCode, setting ginishedStr and finishedIndividualString

        nBuilder.setContentTitle(getString(R.string.eggTimerRunning))
        if (eggCount > 1) {
            nBuilder.setStyle(
                NotificationCompat.BigTextStyle().bigText(
                    getString(R.string.remainingTimeNotification) + " $finishedStr\n" + getString(
                        R.string.nextEggNotification
                    ) + " $finishedIndividualString"
                )
            )
                .setContentText(
                    getString(R.string.remainingTimeNotification) + " $finishedStr\n" + getString(
                        R.string.nextEggNotification
                    ) + " $finishedIndividualString"
                )
        } else {
            nBuilder.setStyle(
                NotificationCompat.BigTextStyle()
                    .bigText(getString(R.string.remainingTimeNotification) + " $finishedStr")
            )
                .setContentText(getString(R.string.remainingTimeNotification) + " $finishedStr")
        }
    } else {
        nBuilder.setContentTitle(getString(R.string.eggTimerDone))
            .setStyle(
                NotificationCompat.BigTextStyle().bigText(getString(R.string.BonAppetite))
            )
            .setContentText(getString(R.string.BonAppetite))
    }

    //irrelevant code

    val nManager =
        applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    nManager.createNotificationChannel("menu_timer", "Timer App Timer", true)

    nManager.notify(135678, nBuilder.build())

    val notification: Notification = nBuilder.build()
    startForeground(135678, notification)
}


private fun hideNotification() {
    val nManager =
        applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    nManager.cancel(135678)
}

private fun startTimer() {
    timer = object : CountDownTimer(secondsRemaining * 1000, 1000) {
        override fun onFinish() = onTimerFinished()

        override fun onTick(millisUntilFinished: Long) {
            secondsRemaining = millisUntilFinished / 1000
            showNotification()
        }
    }.start()
}

private fun onTimerFinished(ring: Boolean = true) {
    if (ring) {
        //irrelevant code, ring and vibrate if timer is finished
    }
    showNotification(true)
}

@TargetApi(26)
private fun NotificationManager.createNotificationChannel(
    channelID: String,
    channelName: String,
    playSound: Boolean
) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val channelImportance = if (playSound) NotificationManager.IMPORTANCE_DEFAULT
        else NotificationManager.IMPORTANCE_LOW
        val nChannel = NotificationChannel(channelID, channelName, channelImportance)
        nChannel.enableLights(true)
        nChannel.lightColor = Color.BLUE
        this.createNotificationChannel(nChannel)
    }
}

private fun <T> getPendingIntentWithStack(
    context: Context,
    javaClass: Class<T>
): PendingIntent {
    val resultIntent = Intent(context, javaClass)
    resultIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP

    val stackBuilder = TaskStackBuilder.create(context)
    stackBuilder.addParentStack(javaClass)
    stackBuilder.addNextIntent(resultIntent)

    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        stackBuilder.getPendingIntent(0, PendingIntent.FLAG_IMMUTABLE)
    } else {
        stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
    }
}
}

提前非常感谢您,我已经尝试修复这个错误好几个星期了,但很难修复,因为一切都对我自己有效。

所以我几乎已经解决了这个问题,不再每周发生数百次崩溃,而是每两天一次。

解决方法是这样的,启动前台服务时:在主线程等待main looper,然后启动即可。更常见的是,现在需要启动服务之前的 5 秒就足够了。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    Handler(Looper.getMainLooper()).post{
        MyContext.getContext().startForegroundService(Intent(this, NotificationUtil::class.java))
    }
} else {
            startService(Intent(this, NotificationUtil::class.java))
}