Android AlarmManager不会在应用程序被终止时触发



我要求每5分钟执行一次任务。我考虑了多个选项,并尝试使用AlarmManager类来触发该任务。然而,当应用程序被终止时,我无法触发警报。

当应用程序打开或在后台运行时,警报似乎完美地工作,但我一退出应用程序,它似乎就完全停止了。

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

我已经用5分钟和10分钟的增量进行了测试,但同样,当应用程序关闭时,它永远不会运行。

请看一下我的实现:

我的活动.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
}

问题是,当应用程序处于后台时,代码正在调用startService()

这在安卓8之前是允许的,但现在受到以下限制:

当应用程序处于前台时,它可以创建和运行前台和后台服务免费。当应用程序进入背景,它有一个几分钟的窗口,在其中它是静止的允许创建和使用服务。在该窗口的末尾,应用程序被认为是空闲的。此时,系统会停止应用程序的后台服务,就像应用程序调用了服务一样Service.stopSelf((方法。

后台执行限制

在上面的示例中创建的服务是";后台服务";因为它不调用startForeground来发布Notification以让用户知道它正在运行。

为了开始一个";前台服务";当应用程序处于后台时,必须使用ContextCompat.startForegroundSerivce。它将在Android 8及以上版本上调用startForegroundService,在旧设备上调用startService

在该服务中,您需要调用startForeground来发布正在进行的服务Notification,以让用户知道该服务正在运行。如果通知没有发布,服务将在5秒钟后终止。

同样值得考虑的是,该任务是否可以通过WorkManager或使用Firebase Cloud Messaging来完成。

最后,你可能需要通知你的客户,运行一个任务";正好每5分钟";在现代安卓设备上是不可能的。我最近没有研究过Doze的实现,但在过去,当使用setExactAndAllowWhileIdle时,在维护窗口期间,我观察到了长达10分钟的例行延迟。但在某些情况下,延迟时间可能会更长。

关于未在BroadcastReceiver:中调用onReceive

禁用电池优化

不要杀死我的应用

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

相关内容

  • 没有找到相关文章

最新更新