我需要每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
}
答案 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:
最后,您可以尝试在getBroadcast
中传递唯一的requestCode,而不是每次都传递0。