为什么在 Android 8.0+ 中 AlarmManager 的 setExact() 行为有所改变?

解读

国内面试官问这道题,并不是想听你背“8.0 开始省电”这种口号,而是考察三点:

  1. 对 Android 后台限制演进史是否熟悉——从 6.0 Doze、7.0 加固,到 8.0 后台执行限制、9.0 应用待机分组,再到 12 前台服务启动限制,能否把 AlarmManager 的改动放进这条时间线;
  2. 是否清楚 setExact() 在 8.0 前后的真实差异——不是“不能用了”,而是“默认不给用,除非满足白名单或声明权限”;
  3. 能否给出国内落地方案——国内无 GMS,厂商 ROM 对后台管控比 AOSP 更激进,如何在不保活、不拉活的前提下,让业务准时唤醒。

知识点

  1. 8.0 行为变更官方描述:targetSdk≥26 的普通应用,调用 setExact() 会抛 SecurityException,除非满足以下任一条件
    • 应用在前台(或前台服务正在运行)
    • 应用进入系统白名单(电池优化白名单、厂商自启动白名单)
    • 持有 SCHEDULE_EXACT_ALARM 权限(普通三方应用无法静态声明,需动态申请并引导用户手动开启)
  2. 系统实现:AlarmManagerService 在 checkExactAlarmPermissionLocked() 中通过 AppOpsManager 检查 OP_SCHEDULE_EXACT_ALARM,未授权直接抛异常;该 op 默认 mode 是 MODE_IGNORED。
  3. 国内差异:华为、OPPO、小米等在 8.0 之前就做了“对齐唤醒”和“后台闹钟限制”,setExact() 即使不抛异常,也会被系统延迟到 5~15 min 的批处理窗口;部分 ROM 把 AlarmManager 的 op 默认设为 MODE_ERRORED,直接崩溃。
  4. 替代策略:
    • 即时场景:前台 Service + setExactAndAllowWhileIdle(),但前台 Service 需挂常驻通知,12+ 还需声明 android:foregroundServiceType="alarm|dataSync" 等;
    • 非即时场景:改用 WorkManager + setExpedited() 或 JobScheduler 加急任务,让系统统一调度;
    • 日历/闹钟类应用:引导用户把应用加入“电池优化白名单”并授予“允许设置精确闹钟”权限,同时提供 Material You 权限申请 UI,降低被拒率;
    • 保活误区:双进程守护、JobScheduler 拉活、推送互相唤醒在 8.0+ 后基本失效,且被应用市场合规扫描直接下架。
  5. 测试验证:adb shell dumpsys alarm | grep -i 包名 查看 alarm batch 状态;adb shell appops get 包名 SCHEDULE_EXACT_ALARM 查看 op 模式;国内还需在真机关闭“省电优化”与“自启动”双开关做对照实验。

答案

Android 8.0 为了进一步降低后台唤醒、延长待机,对 AlarmManager 引入“精确闹钟”权限控制:targetSdk≥26 的普通应用,若不在前台且未被用户授予“允许设置精确闹钟”权限,调用 setExact() 会直接抛 SecurityException。系统通过 AppOpsManager 的 OP_SCHEDULE_EXACT_ALARM 进行校验,默认拒绝。国内无 GMS 环境,厂商 ROM 在 8.0 之前就已对齐唤醒,导致 setExact() 即使不崩溃,也会被批量延迟。因此,业务层应优先使用前台服务 + setExactAndAllowWhileIdle() 或 WorkManager 加急任务,并在设置中引导用户关闭电池优化,才能在国内机型上真正准时唤醒。

拓展思考

  1. Android 12 新增了 SCHEDULE_EXACT_ALARM 权限的“敏感权限”标签,用户在 12+ 首次使用 setExact() 时必须通过系统弹窗显式授权,且系统设置里提供“闹钟与提醒”独立开关。如何设计一次性权限申请路径,使用户在升级 12 后无感知重新获得权限,是日历类 App 的必考题。
  2. 国内厂商在 Android 13 开始把 AlarmManager 对齐窗口拉长到 30 min,且对“加急 Job”也做配额限制。此时若业务强依赖“秒级”触发,需考虑用厂商推送通道的“高优先级消息”作为补充唤醒,但消息体必须携带用户可见内容,否则被认定为恶意拉活。
  3. 从合规角度看,工信部 164 号文明确禁止“频繁唤醒、常驻通知”,未来即使持有 SCHEDULE_EXACT_ALARM 权限,若系统检测到用户 30 天内未主动打开应用,仍可被厂商冻结精确闹钟。因此,架构上应把“准时唤醒”降级为“可接受窗口唤醒”,并通过云端补偿策略保证业务完整性,才是长期可持续方案。