使用 AlarmManager 丢失通知

Missing notifications using AlarmManager

我在使用 AlarmManager 安排多个(最多 14 个)通知时遇到问题。问题是通知随机丢失(特别是当调试电缆未插入时)。例如,前三个通知被触发,在这些通知 none 之后,计划通知的 none 被触发,直到您重新安排通知。好像是通知量的原因,但我不是100%确定。

您可以在下面找到我的安排通知功能(闹钟和就寝时间通知);

private fun createNotificationChannel(name: String, descriptionText: String, id: String, setSound:Boolean)
  {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
    {
      val importance = NotificationManager.IMPORTANCE_DEFAULT
      val channel = NotificationChannel(id, name, importance).apply {
        description = descriptionText
      }
      val audioAttributes = AudioAttributes.Builder()
        .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
        .setUsage(AudioAttributes.USAGE_NOTIFICATION)
        .build()
      if (setSound)
      {
        channel.setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + m_context.packageName + "/" + m_ringtone), audioAttributes)
      }
      (m_context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).createNotificationChannel(channel)
    }
  }

  private fun createBedTimeNotificationChannel()
  {
    createNotificationChannel(BEDTIME_NOTIFICATION_CHANNEL_NAME, BEDTIME_NOTIFICATION_CHANNEL_DESCRIPTION, BEDTIME_NOTIFICATION_CHANNEL, false)
  }

  private fun createWakeUpNotificationChannel()
  {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
    {
      for (channel in (m_context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).notificationChannels)
      {
        if (channel.id.contains(WAKE_UP_ALARM_NOTIFICATION_CHANNEL))
        {
          (m_context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).deleteNotificationChannel(channel.id)
        }
      }
      m_randomNumber = Random().nextLong()
      createNotificationChannel(WAKE_UP_NOTIFICATION_CHANNEL_NAME, WAKE_UP_NOTIFICATION_CHANNEL_DESCRIPTION, WAKE_UP_ALARM_NOTIFICATION_CHANNEL + m_randomNumber, true)
    }
  }

  fun configureAlarmNotification(ringtone: Int, alarmTime: Int, snooze: Boolean, days: BooleanArray)
  {
    m_ringtone = ringtone
    createWakeUpNotificationChannel()
    for ((index, alarmOn) in days.withIndex())
    {
      if (alarmOn)
      {
        val builder = getWakeUpAlarmBuilder()

        val intent = Intent(m_context, CMainActivity::class.java)
        val activity = PendingIntent.getActivity(m_context, WAKE_UP_NOTIFICATION_ID_START + index + 1, intent, PendingIntent.FLAG_UPDATE_CURRENT)
        builder.setContentIntent(activity)

        if (snooze)
        {
          val snoozeIntent = Intent(m_context, CSleepPlannerService::class.java).apply {
            putExtra(ACTION, ACTION_SNOOZE)
            putExtra(NOTIFICATION_ID, WAKE_UP_NOTIFICATION_ID_START + index + 1)
            putExtra(NOTIFICATION, builder.build())
          }

          val snoozePendingIntent: PendingIntent = PendingIntent.getBroadcast(m_context, SNOOZE_PRESSED_NOTIFICATION_ID + index + 1, snoozeIntent, 0)
          builder.addAction(R.drawable.alarm_bel_icon, m_context.getString(R.string.snooze), snoozePendingIntent)
        }

        val notification = builder.build()
        val calendar = Calendar.getInstance()
        val currentTimeInMillis = calendar.timeInMillis

        calendar[Calendar.DAY_OF_WEEK] = convertWeekday(index)
        calendar[Calendar.HOUR_OF_DAY] = alarmTime / 60
        calendar[Calendar.MINUTE] = alarmTime % 60
        calendar[Calendar.SECOND] = 0

        if (calendar.timeInMillis <= currentTimeInMillis)
        {
          calendar.timeInMillis += NUMBER_OF_MILLIS_IN_WEEK
        }

        val pendingIntent = getPendingIntent(calendar.timeInMillis, WAKE_UP_NOTIFICATION_ID_START + index + 1, notification, ACTION_NOTIFICATION, PendingIntent.FLAG_UPDATE_CURRENT)
        (m_context.getSystemService(ALARM_SERVICE) as AlarmManager).setExact(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent)
      }
    }

    val calendar = Calendar.getInstance()
    m_alarmWithSoundEnabledWithinADay.postValue((m_preferences.isAlarmWithSoundEnabled() && isAlarmEnabled(calendar[Calendar.HOUR_OF_DAY] * 60 + calendar[Calendar.MINUTE], m_preferences.getAlarmTime(), calendar[Calendar.DAY_OF_WEEK])))
  }

  fun configureBedTimeNotification(bedTime: Int, bedTimeSetting: Int, days: BooleanArray)
  {
    for ((index, alarmOn) in days.withIndex())
    {
      if (alarmOn)
      {
          val builder = NotificationCompat.Builder(m_context, BEDTIME_NOTIFICATION_CHANNEL).apply {
            setContentTitle(m_context.getString(R.string.bedtime_notification_tile))
            when (BedTimeNotificationSetting_e.fromInt(bedTimeSetting))
            {
              BedTimeNotificationSetting_e.AT_BED_TIME -> setContentText(m_context.getString(R.string.bedtime_notification_at_bedtime))
              BedTimeNotificationSetting_e.TEN_MINUTES_BEFORE -> setContentText(m_context.getString(
                  R.string.bedtime_notification_description_ten
                ))
              BedTimeNotificationSetting_e.THIRTY_MINUTES_BEFORE -> setContentText(m_context.getString(R.string.bedtime_notification_description_thirty))
              BedTimeNotificationSetting_e.FORTYFIVE_MINUTES_BEFORE -> setContentText(m_context.getString(R.string.bedtime_notification_description_fortyfive))
              BedTimeNotificationSetting_e.SIXTY_MINUTES_BEFORE -> setContentText(m_context.getString(R.string.bedtime_notification_description_sixty))
            }
            setAutoCancel(true)
            setSmallIcon(R.drawable.alarm_icon)
            priority = NotificationCompat.PRIORITY_HIGH
          }

        val intent = Intent(m_context, CMainActivity::class.java)
        val activity = PendingIntent.getActivity(m_context, BEDTIME_NOTIFICATION_ID_START + index + 1, intent, PendingIntent.FLAG_UPDATE_CURRENT)
        builder.setContentIntent(activity)
        val notification = builder.build()

        val calendar = Calendar.getInstance()
        val currentTimeInMillis = calendar.timeInMillis

        calendar[Calendar.DAY_OF_WEEK] = convertWeekday(index)
        calendar[Calendar.HOUR_OF_DAY] = bedTime / 60
        calendar[Calendar.MINUTE] = bedTime % 60
        calendar[Calendar.SECOND] = 0

        var newBedTime = calendar.timeInMillis - (bedTimeSetting * 60 * 1000)

        if (newBedTime <= currentTimeInMillis)
        {
          newBedTime += NUMBER_OF_MILLIS_IN_WEEK
        }

        val pendingIntent = getPendingIntent(newBedTime, BEDTIME_NOTIFICATION_ID_START + index + 1, notification, ACTION_NOTIFICATION, PendingIntent.FLAG_UPDATE_CURRENT)
        (m_context.getSystemService(ALARM_SERVICE) as AlarmManager).setExact(AlarmManager.RTC_WAKEUP, newBedTime, pendingIntent)
      }
    }
  }

  private fun getPendingIntent(calendarTime: Long = 0, notificationId: Int, notification: Notification? = null, action: String, flag: Int) : PendingIntent?
  {
    val notificationIntent = Intent(m_context, this::class.java)
    notificationIntent.putExtra(ACTION, action)
    notificationIntent.putExtra(NOTIFICATION_ID, notificationId)
    notificationIntent.putExtra(NOTIFICATION, notification)
    notificationIntent.putExtra(ALARM_TIME, calendarTime)
    return PendingIntent.getBroadcast(m_context, notificationId, notificationIntent, flag)
  }

这可能是什么原因?

很难确切知道哪里出了问题。根据系统的版本,Android 可能会决定等待/延迟/分组通知。剩余电池电量也会影响显示通知的决定。

我看到您在具有 HIGH 值的通知生成器上使用 setPriority。这应该适用于 android < api 26。对于较新的版本,将考虑通知渠道中的 setImportance

https://developer.android.com/reference/androidx/core/app/NotificationCompat.Builder?hl=en#setPriority(int)

  1. 在创建频道时,尝试使用高重要性

val importance = NotificationManager.IMPORTANCE_HIGH // 在 createNotificationChannel 中像这样编辑代码,您当前使用 DEFAULT

  1. 确保在您用来测试的 phone 中没有节电之类的选项。

我使用 AlarmManager 中的函数 setAlarmClock 解决了设置唤醒通知的问题。