【发布时间】:2020-05-21 14:01:55
【问题描述】:
注意:我尝试了 StackOverflow 上的各种解决方案(例如 here)。请不要在未使用我在下面编写的测试中检查您发现的解决方案是否有效的情况下关闭它。
背景
应用程序有一个要求,即用户将提醒设置为在特定时间安排,因此当应用程序在这个时间被触发时,它会在后台执行一些微小的操作(只是一些数据库查询操作),并显示一个简单的通知,以告知提醒。
过去,我使用简单的代码来设置要在相对特定的时间安排的事情:
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 上测试了这段代码,但它似乎没有触发,或者它可能在我提供它很长时间后触发。我很清楚 some OEMs 添加的 terrible behavior 用于从最近的任务中删除应用程序,但这个在模拟器和 Pixel 4 设备(库存)上都有。
我在the docs 上阅读了有关设置警报的信息,它对应用程序进行了限制,因此不会经常发生,但这并没有解释如何在特定时间设置警报,而且没有解释Google's Clock app 是如何成功的。
不仅如此,据我了解,它说限制应该特别适用于设备的低功耗状态,但就我而言,我在设备和设备上都没有这种状态模拟器。我已将警报设置为在大约一分钟后触发。
看到许多闹钟应用不再像以前那样工作,我认为文档中缺少某些内容。此类应用程序的示例是流行的Timely 应用程序bought by Google,但从未获得新的更新来处理新的限制,现在用户want it back.。但是,一些流行的应用程序确实可以正常工作,例如this one。
我尝试过的
为了测试警报确实有效,我会在一分钟后尝试触发警报时执行这些测试,在第一次安装应用程序后,在设备连接到 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 的时钟应用,我没有看到任何特别之处,只是它会在触发前显示通知,而且我也没有在电池优化设置屏幕的“未优化”部分看到它.
看到这似乎是一个错误,我报告了这个here,包括一个示例项目和显示问题的视频。
我检查了模拟器的多个版本,似乎这种行为是从 API 27 (Android 8.1 - Oreo) 开始的。查看at the docs,我没有看到AlarmManager 被提及,而是写了关于各种背景工作的文章。
问题
现在我们如何设置在相对准确的时间触发?
为什么上述解决方案不再起作用?我错过了什么吗?允许?也许我应该使用 Worker 代替?但这不是意味着它可能根本不会按时触发吗?
Google“时钟”应用程序如何克服所有这些问题,并且始终在准确的时间触发,即使它是在一分钟前触发的?仅仅是因为它是一个系统应用程序吗?如果它作为用户应用安装在没有内置它的设备上怎么办?
如果你说它是系统应用,我发现另一个可以在 2 分钟内触发两次警报的应用,here,虽然我认为它有时可能会使用前台服务。
编辑:创建了一个小型 Github 存储库来尝试想法,here。
编辑:最后found a sample 是开源的并且没有这个问题。可悲的是,它非常复杂,我仍然试图弄清楚是什么让它如此不同(以及我应该添加到我的 POC 中的最少代码是什么),让它在从最近的任务中删除应用程序后让它的警报保持预定
【问题讨论】:
-
我在服务上工作了很长时间(我什至不是专业开发人员建议的),但我可以建议你避免使用 alarmManager 来设置低于 5 分钟的闹钟,因为由于 android在后端运行时间服务后的限制,它每 5 分钟或大于不少于 5 分钟后被调用一次。相反,我使用了处理程序。为了在后台继续运行我的服务,我提到了 [github.com/fabcira/neverEndingAndroidService]
-
那么具体的限制是什么?保证触发器在相对精确的给定时间内工作的最短时间是多少?
-
我不记得确切的限制,但是当我在处理它时,我用谷歌搜索了几天以克服后台服务被自动终止的问题。从个人观察我注意到三星、小米等的问题,你不能在 5 分钟的间隔内调用 alarmManger,我有一个使用 alarmManger 实现的数据上传服务,每 1 分钟触发一次,但它让我们抱怨该服务的客户感到失望根本没有运行。对于模拟器来说效果很好。
-
我知道你不能在 android Q 中从后台启动活动,但它看起来不像你的情况。
-
@greeble31 我现在试过了。您认为哪种解决方案有效?由于某种原因,我仍然无法正常工作。我设置了闹钟,我从最近的任务中删除了应用程序,但我没有看到闹钟被触发,即使屏幕已打开并且设备已连接到充电器。它发生在真实设备(带有 Android 10 的 Pixel 4)和模拟器(例如 API 27)上。对你起作用吗?你能分享完整的代码吗?也许在 Github 上?
标签: android alarmmanager