一、清晨的闹钟管理员:AlarmManager 的日常
想象安卓系统是一个庞大的公寓楼,里面住着各种应用程序 “居民”。每个居民有时需要在特定时间做事情,比如早上 7 点提醒用户起床,或者每小时检查一次新消息。这时候,公寓楼里的 “闹钟管理员”——AlarmManager 就登场了。
1.1 管理员上岗:AlarmManager 的启动
清晨,当安卓系统 “公寓楼” 开始苏醒时,系统管家(SystemServer)会第一个起床,然后叫醒各个服务。其中,它会特别提醒:“闹钟管理员,该上班了!” 于是,AlarmManagerService 作为正式的闹钟管理员开始工作:
java
// 系统管家启动闹钟服务
private void startOtherServices() {
mSystemServiceManager.startService(AlarmManagerService.class);
}
这位管理员一上岗,就会先准备好工作工具:创建一个闹钟处理员(AlarmHandler),这个处理员专门负责记录和处理所有闹钟请求,而且它就住在系统管家的主线程 “办公室” 里。
1.2 准备工作:初始化闹钟系统
管理员需要先检查公寓楼的 “闹钟设备” 是否正常。它会打开底层的闹钟驱动(/dev/alarm),就像打开闹钟的机械装置,或者设置电子定时器(timerfd),确保能准确计时:
cpp
运行
// 打开闹钟驱动或设置电子定时器
static jlong init_alarm_driver() {
int fd = open("/dev/alarm", O_RDWR);
return reinterpret_cast<jlong>(new AlarmImplAlarmDriver(fd));
}
同时,管理员会确认当前的时区设置,就像调整闹钟的时间区,确保所有闹钟的时间都是准确的。
二、居民的闹钟请求:PendingIntent 的魔法信封
2.1 魔法信封:PendingIntent 的秘密
公寓里的居民(应用程序)想设置闹钟时,不能直接告诉管理员,而是需要写一封 “魔法信封”(PendingIntent)。这封信里写着:“当闹钟响起时,请帮我做这件事”—— 可以是叫醒广播(发送通知)、启动活动(打开页面)或启动服务(后台任务)。
比如,一个天气应用想每小时更新数据,它会写这样一封信:
java
// 创建魔法信封:当闹钟触发时发送广播
PendingIntent pi = PendingIntent.getBroadcast(
mContext, 0, new Intent(ACTION_WEATHER_UPDATE), 0);
这封信的秘密在于,它其实是一个 “远程任务委托书”。当管理员收到信后,会把它交给系统的 “任务调度中心”(AMS),AMS 会记录下来:“某个应用在某个时间需要做这件事”。
2.2 信封的三种类型
居民可以根据需求选择不同的信封类型:
- 广播信封:像大喇叭,闹钟响时通知所有相关应用 “该干活了”
- 活动信封:像开门钥匙,闹钟响时直接打开某个页面(比如提醒页面)
- 服务信封:像幕后助手,闹钟响时在后台默默完成任务(比如下载数据)
三、设置闹钟:管理员的时间管理术
3.1 提交闹钟请求
居民把魔法信封交给管理员,管理员会根据信封上的时间和要求设置闹钟:
java
// 设置一个闹钟:使用ELAPSED_REALTIME类型,从系统启动开始计时
AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
alarmManager.set(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime(),
pi
);
3.2 闹钟的四种模式
管理员有四种不同的闹钟模式,就像不同功能的闹钟:
- RTC_WAKEUP:绝对时间闹钟,会唤醒手机(比如早上 7 点准时响铃)
- RTC:绝对时间闹钟,但不会唤醒手机(手机休眠时不响)
- ELAPSED_REALTIME_WAKEUP:相对时间闹钟(从系统启动开始算),会唤醒手机
- ELAPSED_REALTIME:相对时间闹钟,不会唤醒手机
3.3 防打扰机制:最小时间间隔
管理员为了避免居民频繁设置闹钟打扰系统,规定了最小时间间隔:
-
当系统 “休息”(idle 状态)时,最小间隔是 9 分钟
-
当系统 “清醒” 时,最小间隔是 5 秒
这就像现实中闹钟不能每分钟都响,避免频繁打扰用户。
四、闹钟响起:任务分发的幕后故事
4.1 后台的闹钟巡逻员
管理员有一个专门的巡逻员线程(AlarmThread),它会不断检查:“有没有闹钟该响了?” 这个巡逻员会盯着电子定时器(timerfd),一旦时间到了,就会触发事件。
java
// 巡逻员线程一直在检查闹钟时间
public void run() {
while (true) {
int result = waitForAlarm(mNativeData);
// 发现有闹钟该响了...
}
}
4.2 执行魔法信封里的任务
当巡逻员发现闹钟时间到了,会通知管理员:“该执行任务了!” 管理员会取出对应的魔法信封,拆开后执行里面的指令:
java
// 分发闹钟事件,执行PendingIntent里的任务
void deliverAlarmsLocked(ArrayList<Alarm> triggerList, long nowELAPSED) {
for (Alarm alarm : triggerList) {
alarm.operation.send(...); // 执行信封里的任务
}
}
如果是广播信封,就会像大喇叭一样发送通知;如果是活动信封,就会打开对应的页面;如果是服务信封,就会启动后台服务。
4.3 灵活的回调机制
有时候,居民不想用魔法信封,而是想直接指定一个 “闹钟回调员”(OnAlarmListener)。比如,系统内部的时间控制器(TimeController)需要定时检查时间,就会直接设置一个回调:
java
// 设置回调:当闹钟触发时,调用onAlarm方法
mAlarmService.set(..., listener, ...);
private final OnAlarmListener mListener = new OnAlarmListener() {
@Override
public void onAlarm() {
checkExpiredDelaysAndResetAlarm(); // 检查并重置闹钟
}
};
五、管理员的智慧:批处理与节能技巧
5.1 批量处理闹钟
管理员发现,如果多个居民的闹钟时间很接近,没必要一个个单独处理,而是可以批量处理。比如,多个闹钟都在 10:00-10:05 之间触发,管理员会把它们归为一批,一起执行,这样可以减少系统开销。
5.2 节能模式:Idle 状态处理
当系统进入 “节能模式”(idle 状态)时,管理员会调整策略:
-
非紧急闹钟会被推迟,避免频繁唤醒系统
-
允许特定的 “白名单” 闹钟在节能模式下提前一点执行,但也有最小间隔限制
这就像晚上睡觉前,把闹钟设置为 “贪睡模式”,减少打扰,同时保证重要的闹钟能准时响。
六、总结:AlarmManager 的工作流程图解
- 应用层:居民(应用)写魔法信封(PendingIntent),交给管理员(AlarmManager)
- 服务层:管理员记录闹钟信息,巡逻员线程定时检查
- 底层:通过驱动或定时器准确计时
- 触发时:执行信封里的任务(广播 / 活动 / 服务)或回调函数
七、常见场景:AlarmManager 的实际应用
-
天气应用:每小时更新数据(设置重复闹钟)
-
闹钟应用:每天早上 7 点响铃(设置精准闹钟)
-
消息应用:定时检查新消息(设置不精准重复闹钟,减少耗电)
-
系统任务:定时清理缓存、同步数据等后台操作
通过这个故事,我们了解到 AlarmManager 就像一个聪明的闹钟管理员,不仅能准确管理所有闹钟请求,还能根据系统状态智能调整,在保证功能的同时尽可能节省资源,是安卓系统中不可或缺的时间管理大师