11、AlarmManager:AlarmManager详解、set() vs setExact() vs setAlarmClock()、省电注意事项
AlarmManager,说白了就是Android系统里的一个“闹钟服务”。
它允许你在指定的时间点,或者经过一段延迟后,执行某个任务。哪怕你的App被杀了,手机在睡觉,它也能把你叫醒。
嗯,这里要注意——它叫“闹钟”,但跟床头那个闹钟不完全一样。它更准确的说法是“定时任务调度器”。
核心一句话:AlarmManager 是 Android 系统级定时器,用于在指定时间触发 PendingIntent。
11.1 AlarmManager 的工作原理
我个人习惯把 AlarmManager 理解成一个“快递员”。
你给它一个包裹(PendingIntent),告诉它几点几分送到。到了时间,它就把包裹扔到你的App门口(触发广播或启动服务)。
但这里有个坑——Android 系统为了省电,会把多个闹钟合并在一起触发。这就是“闹钟对齐”机制。
举个例子:
- 你设了一个10:05的闹钟
- 另一个App设了一个10:07的闹钟
- 系统可能把两个闹钟都拖到10:10一起触发
为什么会这样?因为频繁唤醒CPU太费电了。系统宁愿多睡一会儿,一次性处理完。
我的经验:我在做一款运动健康App时,需要每15分钟记录一次心率。一开始用set(),结果发现记录时间总是漂移。后来才发现是闹钟对齐搞的鬼。
11.2 set() vs setExact() vs setAlarmClock()
这三个方法,是AlarmManager里最常用的。但它们的区别,很多人搞不清楚。
我直接给你一张对比表:
| 方法 | 精度 | 省电 | 唤醒设备 | 典型场景 |
|---|---|---|---|---|
set() |
低(允许对齐) | 最省电 | 否 | 定时刷新、非关键任务 |
setExact() |
高(精确触发) | 较费电 | 否 | 倒计时、限时任务 |
setAlarmClock() |
最高(精确+显示图标) | 最费电 | 是 | 真正的闹钟、提醒 |
11.2.1 set() —— 省电优先
这是最常用的方法。系统会把你的闹钟跟其他闹钟合并,一起触发。
你想想看,如果你只是每隔一小时刷新一下天气数据,完全没必要精确到秒。用set()就够了。
// 设置一个30分钟后的非精确闹钟
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, MyReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + 30 * 60 * 1000,
pendingIntent);
注意:从Android 12(API 31)开始,set()的触发延迟可能高达10分钟。别指望它准时。
11.2.2 setExact() —— 精确但费电
当你需要“差不多准时”时,用setExact()。
它不会跟其他闹钟对齐,但系统仍然可能因为Doze模式而延迟触发。
我曾经在做一个“抢购倒计时”功能时,用了setExact()。结果发现,在Doze模式下,倒计时结束后的弹窗晚了整整2分钟才出现。用户都骂疯了。
// 设置一个精确的5秒后闹钟
alarmManager.setExact(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + 5000,
pendingIntent);
避坑指南:setExact()在Android 6.0以上,如果手机处于Doze模式,仍然会被延迟。要绕过Doze,请使用setExactAndAllowWhileIdle()。
11.2.3 setAlarmClock() —— 真正的闹钟
这个方法,是唯一一个“保证准时”的。
系统会为它做两件事:
- 强制退出Doze模式
- 在状态栏显示一个闹钟图标
说白了,这就是给“用户明确期望”的闹钟用的。比如早上7点的起床闹钟。
// 设置一个真正的闹钟
AlarmManager.AlarmClockInfo alarmClockInfo =
new AlarmManager.AlarmClockInfo(System.currentTimeMillis() + 10000, pendingIntent);
alarmManager.setAlarmClock(alarmClockInfo, pendingIntent);
重要:setAlarmClock()会显示闹钟图标,用户一看就知道你App设了闹钟。别滥用,否则用户会卸载你。
11.3 省电注意事项
AlarmManager用不好,就是电老虎。我见过太多App因为滥用AlarmManager,被用户投诉“耗电快”。
这里我总结几条铁律:
- 能用set()就别用setExact() —— 非关键任务,让系统帮你对齐。
- 能用setExact()就别用setAlarmClock() —— 除非你真的需要闹钟图标。
- 减少唤醒频率 —— 能5分钟一次就别1分钟一次。电池经不起折腾。
- 使用批量闹钟 —— 如果你有多个定时任务,尽量合并成一个PendingIntent。
- 取消不再需要的闹钟 —— 用
alarmManager.cancel(pendingIntent)及时清理。
我的血泪教训:曾经有一个版本,我在App里设了20多个闹钟,分别用于不同功能。结果用户反馈手机待机时间直接砍半。后来我全部改成set(),并合并成3个闹钟,耗电问题才解决。
11.4 知识体系图
下面这张图,帮你理清AlarmManager的核心逻辑:
11.5 实战建议
最后,给你几个我在项目中实际用到的建议:
- 定时刷新数据:用set() + JobScheduler 配合,别只用AlarmManager。
- 倒计时功能:用setExact(),但记得在onReceive里重新设置下一次。
- 用户主动设置的提醒:用setAlarmClock(),这是唯一让用户信任的方式。
- 测试时:用
adb shell dumpsys alarm查看所有闹钟列表,排查问题。
我的习惯:每次发布前,我都会用dumpsys检查一遍App注册的闹钟数量。超过5个,我就开始优化。毕竟,用户手机的电池,比你的KPI重要得多。
公众号:蓝海资料掘金营,微信deep3321