API文档解读
概述:
这个类提供了一种系统的提示服务。他允许你安排你的应用在未来某个时间点的状态。当一个闹钟开始的时候,他内部通过系统广播注册的Intent会自动启动目标的应用(如果它不在运行的话)。当设备休眠时,已经被注册的闹钟会被保留(在运行时,他可以任意唤醒设备),但是当他被关闭或者重启时他所携带的信息将被清空。
主要方法介绍
AlarmManager为系统服务,使用AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
获取AlarmManager对象。
常用的方法有三个:
set(int type,long triggerAtMillis,PendingIntent operation)
单次闹钟设置。
首先看一下参数:
type: 闹钟类型
- ELAPSED_REALTIME: 使用
SystemClock.elapsedRealtime()
计时(相对时间),也就是从系统启动后才开始计时的,包括睡眠时间,使用这种方式时,系统进入睡眠状态时,闹铃不会唤醒系统,直到系统下次被唤醒才传递。 - ELAPSED_REALTIME_WAKEUP:跟上面ELAPSED_REALTIME类型相似,不同点是当系统进入睡眠状态时,闹铃依然会唤醒系统。
- RTC:使用
System.currentTimeMillis()
(绝对时间)计时,当triggerAtMillis值与当前系统时间相同时,启动闹钟。当系统进入睡眠状态时,这种类型的闹铃不会唤醒系统。 - RTC_WAKEUP:跟RTC类型类似,不同点是当系统进入睡眠状态时,这种类型的闹铃会唤醒系统。
triggerAtMillis: 闹钟开始启动的时间
operation: 闹钟开始时响应的操作,将包含触发操作的Intent封装到PendingIntent中,并将此PendingIntent当做参数传入。它的获取方式取决于实现闹钟的方式:如果你用Service启动闹钟的话,就是用Pending.getService()方式封装,同理:Broadcast对应PendingIntent.getBroadcast(),Activity对应PendingIntent.getActivity()。
setRepeating(int type,long triggerAtMillis,long intervalTime,PendingIntent operation)
设置重复闹钟。
intervalTime:闹钟两次唤醒的时间间隔
setInexactRepeating(int type,long triggerAtMillis,long intervalTime,PendingIntent operation)
设置不精确重复闹钟,相对于setRepeating()它更节能省电巴拉巴拉,但是间隔的时间不固定。
源码分析
无论是set()还是setRepeating()方法,最后都调用了setImpl()方法,只是传入的参数不同,调用停止在mService.set(type, triggerAtMillis, windowMillis, intervalMillis, operation,
这个方法,这个mService是什么鬼,我们找到它的初始化部分:
workSource, alarmClock);
/**
* package private on purpose
*/
AlarmManager(IAlarmManager service, Context ctx) {
mService = service;
final int sdkVersion = ctx.getApplicationInfo().targetSdkVersion;
mAlwaysExact = (sdkVersion < Build.VERSION_CODES.KITKAT);
}
原来它是IAlarmManager,因为无法查看他的内部实现,我在网上寻觅了一下,果然找到一篇关于他的介绍参考。
ALMS(疯狂借鉴中)
在Android中,闹钟是由Alarm Manager Service控制的,我们调用的AlarmManager其实只是个打杂的,AlarmManager里面控制的主要是逻辑部分,然后通过调用ALMS提供的接口来完成具体的功能,ALMS的外部接口为IAlarmManager,所以具体的代码实现都是在ALMS内部的。
ALMS的接口:
interface IAlarmManager {
void set(int type, long triggerAtTime, in PendingIntent operation);
void setRepeating(int type, long triggerAtTime, long interval, in PendingIntent operation);
void setInexactRepeating(int type, long triggerAtTime, long interval, in PendingIntent operation);
void setTime(long millis);
void setTimeZone(String zone);
void remove(in PendingIntent operation);
}
这样的方法名是不是十分熟悉!!那么他们是怎么实现的呢?
在AlarmManagerService类中,有四个List,分别对应上文说的四种模式的闹钟,来偷个图。
五颜六色的是我们在程序中新建的各种闹钟,下面的蓝色的是Framework层对应的包含了某一种类的全部的闹钟的ArrayList。如:ArrayList中,五颜六色的为Alarm实体,蓝色的为ArrayList。
主要的方法实现:
public void setRepeating(int type, long triggerAtTime,
long interval,
PendingIntent operation)
{
if (operation == null) {
Slog.w(TAG, "set/setRepeating ignored because there is no intent");
return;
}
synchronized (mLock) {
Alarm alarm = new Alarm();
alarm.type = type;
alarm.when = triggerAtTime;
alarm.repeatInterval = interval;
alarm.operation = operation;
// Remove this alarm if already scheduled.
removeLocked(operation);
if (localLOGV) Slog.v(TAG, "set: " + alarm);
int index = addAlarmLocked(alarm);
if (index == 0) {
setLocked(alarm);
}
}
}
新建一个Alarm,将对应的参数传入,如果当前List中存在此Alarm就删除旧的,将新的Alarm通过addAlarmLocked方法添加到对应的ArrayList中。
private int addAlarmLocked(Alarm alarm) {
ArrayList<Alarm> alarmList = getAlarmList(alarm.type);
int index = Collections.binarySearch(alarmList, alarm, mIncreasingTimeOrder);
if (index < 0) {
index = 0 - index - 1;
}
if (localLOGV) Slog.v(TAG, "Adding alarm " + alarm + " at " + index);
alarmList.add(index, alarm);
. . . . . .
return index;
}
在addAlarmLocked方法中使用二分法进行排序,激发时间越早的将被放到前面。如果插入到第0位,就会调用setLocked更新一下对应ArrayList的最早激发时间。
在AlarmManagerService内部有一个AlarmThread线程,初始化的时候就启动这个线程。
AlarmThread新建了一个List来存储所有类型的需要激发的Alarm,即triggerList,然后遍历调用triggerList中的每个PendingIntent,然后判断一下闹钟的类型是不是唤醒类型(设备睡眠的时候是否需要唤醒),如果需要唤醒就调用BatteryStatsService内部方法进行唤醒。在AlarmManagerService内部有个变量为mBroadcastRefCount,他记录了triggerList的大小,每激活一个PendingIntent就会将mBroadcastRefCount减一,直到mBroadcastRefCount为0时就释放掉锁。