注意:我尝试了各种关于StackOverflow的解决方案(此处为 example )。请不要在未检查发现的解决方案是否使用我在下面编写的测试中起作用的情况下关闭此功能。
背景
该应用程序有一个要求,即用户将提醒设置为在特定时间进行安排,因此,当该应用程序在此时间触发时,它会在后台执行一些微小的操作(只是一些数据库查询操作),并显示一个简单的通知,告知有关提醒。
过去,我使用简单的代码来设置要在相对特定的时间安排的内容:
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val pendingIntent = PendingIntent.getBroadcast(context, requestId, Intent(context, AlarmReceiver::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
when {
VERSION.SDK_INT >= VERSION_CODES.KITKAT -> alarmManager.setExact(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
else -> alarmManager.set(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
}
class AlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d("AppLog", "AlarmReceiver onReceive")
//do something in the real app
}
}
用法:
val timeToTrigger = System.currentTimeMillis() + java.util.concurrent.TimeUnit.MINUTES.toMillis(1)
setAlarm(this, timeToTrigger, 1)
问题
我现在已经在新的Android版本和带有Android 10的Pixel 4的模拟器上测试了此代码,它似乎没有触发,或者自从我提供它以来经过了很长时间才触发。我深知的可怕行为是一些OEM厂商加入到去除近期任务的应用程序,但是这一次是两个仿真器和像素4装置(股票)。
我已经阅读了有关设置警报的文档,该警报受到应用程序的限制,因此不会经常发生,但这并不能解释如何在特定时间设置警报,也不能解释Google的Clock应用程序如何成功做到这一点。
不仅如此,而且据我了解,它说应该对设备特别是低功耗状态应用限制,但就我而言,我在设备和仿真器上都没有这种状态。我将警报设置为从现在开始大约一分钟后触发。
看到许多闹钟应用无法像以前那样工作了,我认为文档中缺少某些内容。这样的应用程序的例子是流行的Timely应用程序,该应用程序已被Google收购,但从未获得新的更新来处理新的限制,现在用户希望将其取回。。但是,某些流行的应用程序确实可以正常工作,例如this。
我尝试过的
为了测试警报的确有效,在首次安装该应用程序之后的一分钟之后,我尝试在从现在开始的一分钟内触发警报,并在设备连接到PC的整个过程中进行了这些测试(以查看日志):
- 测试应用何时位于用户可见的前台。-花了1-2分钟。
- 测试将应用发送到后台的时间(例如,使用主屏幕按钮)-大约花费了1分钟
- 测试何时将应用程序的任务从最近的任务中删除。-我等待了20多分钟,却没有看到警报被触发,没有写入日志。
- 像#3一样,也可以关闭屏幕。可能会更糟...
我尝试使用接下来的东西,所有的东西都不起作用:
alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
以上任何一项与以下各项的组合:
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) alarmManager.setWindow(AlarmManager.RTC_WAKEUP, 0, 60 * 1000L, pendingIntent)
尝试使用服务而不是BroadcastReceiver。还尝试了不同的过程。
试图使该应用程序从电池优化中被忽略(这无济于事),但是由于其他应用程序不需要它,因此我也不应该使用它。
尝试使用此:
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP)
alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
- 试图拥有一个将触发onTaskRemoved触发器的服务,以在那里重新安排警报,但这也无济于事(尽管该服务运行良好)。
至于Google的Clock应用程序,除了在触发之前会显示通知,我没有看到其他特别的东西,而且在电池优化设置屏幕的“未优化”部分也没有看到它。
看到这似乎是一个错误,我在这里报告了这个问题,包括一个示例项目和演示该问题的视频。
我检查了模拟器的多个版本,看来此行为始于API 27(Android 8.1-Oreo)。看一下文档,我没有看到AlarmManager被提及,而是写了有关各种背景工作的文章。
问题
我们如何设置如今要在相对准确的时间触发的内容?
上述解决方案为何不再起作用?我有什么想念的吗?允许?也许我应该改为使用工人?但是那不是意味着它可能根本不会按时触发吗?
Google“时钟”应用程序如何克服所有这些问题,并且无论如何都会始终在准确的时间触发,即使它是在一分钟前触发的呢?仅仅是因为它是一个系统应用程序吗?如果将其作为用户应用安装在没有内置设备的设备上怎么办?
如果你说,这是因为它是一个系统的应用程序中,我发现了另一个应用程序,可以在2分钟内两次触发报警,在这里,但我认为它可能有时会使用前景的服务。
编辑:在这里做了一个很小的Github存储库来尝试想法。
编辑:终于找到了一个既开源又没有这个问题的示例。遗憾的是,这非常复杂,我仍然试图找出是什么使它如此不同(以及我应该添加到POC中的最小代码是什么),以使其在从近期任务中删除该应用程序后使警报保持预定状态