为什么我收到 creating/updating 通知的 RemoteServiceException?

Why do I get RemoteServiceException for creating/updating a notification?

背景

我有一个业余时间开发的应用程序,我已经开发了几年 (here),我会尽可能多地处理崩溃(我讨厌看到崩溃报告!)。

问题

最近的一次崩溃似乎很新,是这个:

android.app.RemoteServiceException: 
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1768)
  at android.os.Handler.dispatchMessage (Handler.java:106)
  at android.os.Looper.loop (Looper.java:164)
  at android.app.ActivityThread.main (ActivityThread.java:6494)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:438)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:807)

这就是我所看到的...

它似乎只出现在 Android 8.1 中。由于该应用程序必须非常受欢迎,我非常确定它仍然只会在 Android 8.1.

上出现

我试过的

在网上搜索,我能找到类似的崩溃,但在所有这些中,他们都有更多的线索。

所以我试着回忆一下我最近做了哪些改变。唯一的变化是将支持库迁移到 Android-X。其余的变化非常小,我不认为它们是造成这种情况的原因。

因为我没有任何进一步的线索,所以我决定报告 Android-X 部分 here(如果需要,那里有更多信息),因为我不相信它是因为我试图解决问题,它看起来非常具体。

后来我集成了 Firebase Crashlytics,并在每个这样的日志上方得到了这个微小的额外线索:

Fatal Exception: android.app.RemoteServiceException
Bad notification for startForeground: java.lang.RuntimeException: invalid channel for service notification: Notification(channel=channel_id__app_monitor pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x40 color=0x00000000 category=service vis=SECRET)

这很奇怪,因为我确实正确打开了服务,否则它根本无法工作,特别奇怪的是,这只发生在 Android 8.1 上,而不是例如 8.0 , 与如何启动前台服务有相同的要求。

这个有问题的通知是我用来全局监控与应用程序相关的事件的前台服务(仅来自 Android O,它有 BroadcastReceivers 的限制)。它在 2 个案例上得到更新:

  1. 语言环境更改
  2. 在某些时候出现的对话框上勾选复选框(单击对话框时)。

当我测试这两个时,它们在 Android 8.0 和 8.1 上都工作正常。

这是 create/update 通知的代码:

    @JvmStatic
    fun getUpdatedAppMonitorNotification(context: Context): Notification {
        val builder = Builder(context, context.getString(R.string.channel_id__app_monitor)).setSmallIcon(R.drawable.ic_stat_app_icon)//
                .setPriority(Notification.PRIORITY_DEFAULT).setCategory(Notification.CATEGORY_SERVICE)
        builder.setVisibility(NotificationCompat.VISIBILITY_SECRET)
        builder.setShowWhen(false)
        if (!PreferenceUtil.getBooleanPref(context, R.string.pref__avoid_showing_app_monitor_notification_dialog, false))
            builder.setContentTitle(context.getString(R.string.app_monitor__notification_title))
        if (VERSION.SDK_INT < VERSION_CODES.P) {
            val mainIntent = Intent(context, MainActivity::class.java)
                    .putExtra(MainActivity.EXTRA_OPENED_FROM_NOTIFICATION, true)
            builder.setContentIntent(PendingIntent.getActivity(context, 0, mainIntent, PendingIntent.FLAG_UPDATE_CURRENT))
        } else {
            val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
                    .putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
                    .putExtra(Settings.EXTRA_CHANNEL_ID, context.getString(R.string.channel_id__app_monitor))
            val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
            builder.setContentIntent(pendingIntent)
        }
        return builder.build()
    }

以及我在服务中使用的代码:

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    NotificationId.initNotificationsChannels(this)
    val notification = getUpdatedAppMonitorNotification(this)
    startForeground(NotificationId.APP_MONITOR, notification)
    ...
    return super.onStartCommand(intent, flags, startId)
}

这是我从其他地方调用的代码,用于更新通知:

    @JvmStatic
    fun updateAppMonitorNotification(context: Context) {
        if (VERSION.SDK_INT < VERSION_CODES.O)
            return
        val notification = AppMonitorService.getUpdatedAppMonitorNotification(context)
        val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.notify(NotificationId.APP_MONITOR, notification)
    }

问题

  1. 为什么会发生?是关于 Android X 吗?还是通知楼有问题?

  2. 为什么它只出现在 Android 8.1 上?

好的,我仍然不知道为什么会发生这种情况,但我找到了一个解决方法,就是让服务完成其通知的所有更新,只需通过 context.startForegroundService函数。

到目前为止,通过使用它,我仍然没有看到任何此类崩溃。

我认为这是问题所在:

.putExtra(Settings.EXTRA_CHANNEL_ID, context.getString(R.string.channel_id__app_monitor))

您需要输入(不变的)频道 ID,而不是本地化的频道名称

如果您能够重现此问题,您还可以查看 AppSettings.*[您的应用] 的错误报告,看看是否有 ID 与该频道匹配的频道你正在尝试使用。