应用被杀死时,Android AlarmManager不会触发

时间:2020-08-07 15:19:15

标签: android kotlin alarmmanager foreground-service repeatingalarm

我需要每5分钟执行一次任务。我考虑了多个选项,并尝试使用AlarmManager类来实现此任务以触发任务。但是,当应用程序被终止时,我无法触发警报。

当应用打开或在后台运行时,警报似乎无故障运行,但是一旦我退出应用,警报似乎就会完全停止。

我的实现是使用setExactAndAllowWhileIdle()函数并自行处理此重复。最初的警报会在5秒后触发,然后每5分钟触发一次。

我已经以5分钟和10分钟的增量对其进行了测试,但是再次关闭该应用程序后再也无法运行。

请看一下我的实现:

我的Activity.kt:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_landing)
    initAlarm()
}

private fun initAlarm() {
    val alarm = getSystemService(Context.ALARM_SERVICE) as AlarmManager

    val intent = Intent(this, AlarmReceiver::class.java).apply { action = "MY_ALARM" }
    val sender = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)

    val ALARM_DELAY_IN_SECOND = 5
    val alarmTimeAtUTC = System.currentTimeMillis() + ALARM_DELAY_IN_SECOND * 1_000

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        Log.(TAG, "initAlarm() 23+ - $alarmTimeAtUTC")
        alarm.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, alarmTimeAtUTC, sender)
    } else {
        alarm.setExact(AlarmManager.RTC_WAKEUP, alarmTimeAtUTC, sender)
    }
}

AlarmReceiver.kt:

class AlarmReceiver : BroadcastReceiver() {

    companion object {
        private val TAG = AlarmReceiver::class.java.simpleName
    }

    override fun onReceive(context: Context?, intent: Intent?) {
        Log.d(TAG, "onReceive()")
        if (intent?.action == "MY_ALARM") {
            Log.d(TAG, "onReceive() - starting service")
            context?.startService(Intent(context, MyService::class.java))
            initAlarm(context)
        }
    }

    private fun initAlarm(context: Context?) {
        val alarm = context?.applicationContext?.getSystemService(Context.ALARM_SERVICE) as AlarmManager

        val intent = Intent(context, AlarmReceiver::class.java).apply { action = "MY_ALARM" }
        val sender = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)

        val ALARM_DELAY_IN_SECOND = 600
        val alarmTimeAtUTC = System.currentTimeMillis() + ALARM_DELAY_IN_SECOND * 1_000

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            Log.d(TAG, "initAlarm() 23+ - $alarmTimeAtUTC")
            alarm.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, alarmTimeAtUTC, sender)
        } else {
            alarm.setExact(AlarmManager.RTC_WAKEUP, alarmTimeAtUTC, sender)
        }
    }
}

MyService.kt:

override fun onCreate() {
    super.onCreate()
    Log.d(TAG, "onCreate()")
    doMyTask()
}

private fun doMyTask() {
    job = CoroutineScope(Dispatchers.IO).launch {
            // Perform task here and once complete stop service
            stopSelf()
        }
    }
}

override fun onDestroy() {
    super.onDestroy()
    Log.d(TAG, "onDestroy()")
    job?.cancel()
    job = null
}

1 个答案:

答案 0 :(得分:2)

问题是该应用在后台时代码正在调用startService()

在Android 8之前允许这样做,但现在受到以下限制:

应用程序位于前台时,它可以创建并运行 免费提供前台和后台服务。当应用进入 背景,它有一个几分钟的窗口,它仍然在 允许创建和使用服务。在该窗口的末尾,该应用 被认为是空闲的。此时,系统停止了该应用的 后台服务,就好像该应用已将这些服务称为“ Service.stopSelf()方法。

Background Execution Restrictions

在以上示例中创建的服务是Android文档语言中的“后台服务”,因为它不会调用startForeground来发布Notification来使用户知道它正在运行

要在应用程序处于后台状态时从警报启动“前台服务”,必须使用ContextCompat.startForegroundSerivce。在Android 8及更高版本上,它将调用startForegroundService,在较旧的设备上,它将调用startService

在服务中,您将需要呼叫startForeground来发布正在进行的服务Notification,以使用户知道该服务正在运行。如果未发布通知,则该服务将在5秒钟后被终止。

还值得考虑是通过WorkManager还是Firebase Cloud Messaging来完成任务。

最后,您可能需要通知客户端,在现代Android设备上不可能“每5分钟恰好”运行任务。我最近没有看过Doze的实现,但是在过去,观察到使用setExactAndAllowWhileIdle时在维护窗口期间的例行延迟最多会增加10分钟。但是在某些情况下,延迟可能会更长。

关于没有在BroadcastReceiver中调用onReceive:

Disable Battery Optimisations

Do not kill my app

最后,您可以尝试在getBroadcast中传递唯一的requestCode,而不是每次都传递0。