通过 AlarmManager 创建的警报在 25 天后未运行(= 2^31 - 1 = 2147483647 毫秒)

Kum*_* C. 5 android alarmmanager android-7.1-nougat

背景: 我有一个在 Android 7.1 上运行的 Android 应用程序。使用 AlarmManage 设置重复任务,以便每天在特定时间执行任务。代码如下:

AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
long startInMs = getComing9AM();
Intent intent = ...; //an intent to be run when alarm time is up.
PendingIntent ScheduledIntent = PendingIntent.getBroadcast(context, id, intent, 0);
am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, startInMs, ScheduledIntent);


private long getComing9AM(){
    long now = System.currentTimeMillis();
    long today = getToday9AM();
    return (now < today? today:getTomorrow9AM());
}

private long getToday9AM(){
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(System.currentTimeMillis());
    calendar.set(Calendar.HOUR_OF_DAY, 9);
    calendar.set(Calendar.MINUTE, 0);
    calendar.set(Calendar.SECOND, 0);
    return calendar.getTimeInMillis();
}

private long getTomorrow9AM(){
    return getToday9AM() + 7 * 24 * 3600 * 1000;
}
Run Code Online (Sandbox Code Playgroud)
  1. android设备不是手机,而是一个小盒子。正常运行时,只需插入电源线即可连接WIFI。

问题描述:

  1. 计划任务每​​天都运行良好。三周或更长时间后,任务不再运行。
  2. 所有其他功能仍然有效,包括 MQTT 客户端通信、IO 操作。

分析结果:

  1. 检查问题模式后,发现自上次启动后约 25 天,该任务将无法工作。作为对数字敏感的数学极客,“25 天”= 2,160,000 秒 = 2,160,000,000 毫秒。而这个值非常接近于 2^31-1 (Integer.MAX_VALUE),相当于 24.85 天。

  2. HDMI 线已插入设备,屏幕显示无输入。

  3. 即使在 25 天后(通过 HTTPS)分配了新的计划任务,该任务也不会执行。

  4. 将 USB 设备(我尝试使用鼠标)插入设备后,Android 被唤醒(屏幕有显示)。并且Android已经唤醒,任务可以每天正常运行,直到接下来的25天。

  5. 使用ADB检查Android设置,我只能发现一个参数值是Integer.MAX_VALUE。即(adb shell dumpsys power):

    mMaximumScreenOffTimeoutFromDeviceAdmin=2147483647 (enforced=false) 屏幕关闭超时:2147483647 ms

  6. 根据发现 #5,我尝试将屏幕关闭超时设置为 60000 以尝试重现该问题。但是任务仍然可以照常运行。

  7. 它不应该与打盹模式相关,因为设备总是在充电并且没有电池。

  8. (新发现)dumpsys power 中“mWakefulness”的值是 24.85 天后的睡眠。在 24.85days 之前,这个值应该是Awake

  9. (新发现)鉴于 wWakefulness=Asleep arelay,“mLastSleepTime”=now - (startupTimestamp + 24.85day)。

寻求建议:

  1. 我想有一些方向来进一步调查这个问题。

  2. 我想知道是否有办法在应用程序中模拟唤醒事件?(作为找到根本原因之前的解决方法)。

Mar*_*ini 0

我想得到一些指导来进一步调查这个问题。

我会找到对代码进行单元测试的方法,以确保其正常工作。您可以定期激发意图并尝试一下。在单元测试中或通过adb。您提到“通过 https”,但也许您需要直接进入“金属”并自己触发方法进行测试。

我想知道是否有办法在应用程序中模拟唤醒事件?(作为查找根本原因之前的解决方法)。

您的设备不是手机,所以我不确定它是否能够接收 Firebase 推送通知(或任何形式的通知),因为您需要 Google Play 服务来实现此目的,并且还附带了另一包东西,但我会尝试手动控制这些事件(外部)以确保您的代码不会导致问题。每天上午 9 点发出“嘟嘟声”(或打印到 logcat)是否仍然有效?

如果 AlarmManager 在 NN 天后以某种方式周期性地中断,那么有些东西要么真的坏了(说实话,这很奇怪,7.1 相当旧并且已经使用了很长时间,有人会对此说些什么,我希望(? )),或者你的代码行为不当。

开始隔离事物,您可以创建一个简单的应用程序,它所做的只是每天上午 9:30 打印日志。还有效吗?

或者工作经理会在这里帮助您还是会有其他您无法使用/拥有的依赖项?当然,这需要权衡,因为 WorkManager 与 AlarmManager 不同,并且受到更多限制。

即使在 25 天后分配新的计划任务(通过 HTTPS),该任务也不会执行。

这也令人费解,因为 AlarmManager 似乎完全停止工作和接收工作。adb如果您此时手动广播意图,这项工作是否有效?您有不止一台这样的设备吗?所有人都会遇到这种情况吗?

这是一个非常有趣的问题,我希望我有更具体的东西,但很少有人会知道与谷歌试图在手机上“节省电池”的所有新方法相关的内部工作原理AlarmManager

setExactAndAllowWhenIdle描述为:

与其他警报不同,系统可以自由地重新安排此类警报,使其与任何其他警报(甚至来自同一应用程序的警报)不按顺序发生。当设备空闲时,这种情况显然会发生(因为该警报可以在空闲时关闭,此时应用程序中的任何其他警报将保留到稍后),但即使在不空闲时也可能发生。请注意,与常规的精确警报相比,操作系统将允许自己更灵活地安排这些警报,因为应用程序已选择此行为。当设备空闲时,它可能会采取更多的调度自由来优化电池寿命。

操作系统是否可以允许自己以某种形式灵活并忽略您的警报?嗯不确定。

最后的评论,您看过AlarmManager 的源代码吗?也许这里面有暗示。

最后,这篇长文(应该是评论)并没有真正给你一个解决方案,只是一个让我倾诉想法的开放场所。:) 如果需要的话我会更新;请告诉我们您发现了什么:)

作为旁注:我会测试您是否可以使用 Gradle 4.x 并有权访问 Java 8 API java.time(比日历甚至 util.Date 好得多),或者尝试利用(现已存档/弃用,但仍然可以使用一段时间,直到 Gradle 4.x 被“简化”)来自 Wharthon 的 ThreeTenABP 抽象使用相同的 Java8 API(从 ThreeTen 迁移到java.time实际上是一个 10 分钟的查找/替换来更改导入)。

简而言之,您可以将所有日历废话替换为

val tomorrowAt9Am = ZonedDateTime.now().plusDays(1).withHour(9).withMinute(0).withSecond(0).toEpochSecond()

编辑:你发现的最后两点(mWakeiness)非常有趣,我会继续寻找。与此同时,我在 Android 代码库中发现了该变量的一些实例。最有趣的是来自 AttentionDetector 的这个(多名字啊);随意浏览所有结果