Alarm用于执行某些需要在应用程序生命周期之外执行的操作,比如,在应用程序中设置一个alarm来控制在每天的特定时刻运行一个service, 那个时刻应用程序本身可能已经关闭。
Android提供了AlarmManager类给应用程序作为接口,来进行alarm相关的操作。AlarmManager中提供的接口中,比较常用的是set()、setRepeating()、cancel()。下面我们重点看一下set()的流程。
Setup 1 调用AlarmManage的set()
public void set(int type, long triggerAtMillis, PendingIntent operation) {
try {
mService.set(type, triggerAtMillis, operation);
} catch (RemoteException ex) {
}
}
Setup 2 调用AlarmManagerService的set()
public void set(int type, long triggerAtTime, PendingIntent operation) {
setRepeating(type, triggerAtTime, 0, operation);
}
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;
}
type=overridenAlarmType(type);
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);
}
}
}
setRepeating()接收四个参数type、triggerAtTime、interval、operation。
Type表示alarm的类型,Android提供四种类型的alarm:
1、ELAPSED_REALTIME_WAKEUP 可以在休眠时唤醒设备,采用的时间是相对时间,从系统启动后开始计时。
2、ELAPSED_REALTIME 在休眠时不可以唤醒设备,采用的时间是相对时间,从系统启动后开始计时。
3、RTC_WAKEUP 可以在休眠时唤醒设备,采用的时间是绝对时间,即UTC时间。
4、RTCE 在休眠时不可以唤醒设备,采用的时间是绝对时间,即UTC时间。
triggerAtTime表示设定的操作运行的时间。
Interval表示每次运行的时间间隔,该值为0就表示这个操作只执行一次。
Operation表示指定的操作,以intent的形式来启动该操作。
type=overridenAlarmType(type); 主要针对设置RTC_WAKEUP或者ELAPSED_REALTIME_WAKEUP类型的alarm的情况,因为_WAKEUP 类型的alarm能在休眠的时候唤醒设备,所以不能让所有程序都可以设置这种alarm, 除了owner为系统用户(uid在100000—19999范围内的用户)和etc/alarm_allow.xml列表中指定的程序可以设置_WAKEUP类型的alarm, 其他的程序的type如果为_WAKEUP类型,就在这里强制转换成了相应的非WAKEUP类型。
然后生成一个alarm类的对象, 如果该alarm的操作已经在相应type的操作队列中,要先删除该操作,然后再添加。
Setup 3 调用setLocked(alarm)
private void setLocked(Alarm alarm) {
if (mDescriptor != -1)
{
// The kernel never triggers alarms with negative wakeup times
// so we ensure they are positive.
long alarmSeconds, alarmNanoseconds;
if (alarm.when < 0) {
alarmSeconds = 0;
alarmNanoseconds = 0;
} else {
alarmSeconds = alarm.when /1000;
alarmNanoseconds = (alarm.when% 1000) * 1000 * 1000;
}
set(mDescriptor, alarm.type, alarmSeconds, alarmNanoseconds);
}
else
{
Message msg = Message.obtain();
msg.what = ALARM_EVENT;
mHandler.removeMessages(ALARM_EVENT);
mHandler.sendMessageAtTime(msg, alarm.when);
}
}
mDescriptor被设置为打开/dev/alarm的描述符,如果alarm设备存在,就利用alarm设备实现alarm的功能,如果alarm设备不存在,就发消息给AlarmHandler类来作一次trigger的动作(不太明白跑这个分支的作用)。
Setup 4 利用/dev/alarm实现设置一个alarm的功能
static void android_server_AlarmManagerService_set(JNIEnv* env, jobject obj, jint fd, jint type, jlong seconds, jlong nanoseconds)
{
struct timespec ts;
ts.tv_sec = seconds;
ts.tv_nsec = nanoseconds;
int result = ioctl(fd, ANDROID_ALARM_SET(type), &ts);
if (result < 0)
{
ALOGE("Unable to set alarm to %lld.%09lld: %s\n", seconds, nanoseconds, strerror(errno));
}
}
Setup 5 利用/dev/alarm触发一个alarm的功能。
public void run()
{
while (true)
{
int result = waitForAlarm(mDescriptor);
ArrayList triggerList = new ArrayList();
if ((result & TIME_CHANGED_MASK) != 0) {
remove(mTimeTickSender);
mClockReceiver.scheduleTimeTickEvent();
Intent intent = new Intent(Intent.ACTION_TIME_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
| Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
synchronized (mLock) {
final long nowRTC = System.currentTimeMillis();
final long nowELAPSED = SystemClock.elapsedRealtime();
if (localLOGV) Slog.v(
TAG, "Checking for alarms... rtc=" + nowRTC
+ ", elapsed=" + nowELAPSED);
if ((result & RTC_WAKEUP_MASK) != 0)
triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
if ((result & RTC_MASK) != 0)
triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
if ((result & ELAPSED_REALTIME_WAKEUP_MASK) != 0)
triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowELAPSED);
if ((result & ELAPSED_REALTIME_MASK) != 0)
triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowELAPSED);
// now trigger the alarms
Iterator it = triggerList.iterator();
while (it.hasNext()) {
Alarm alarm = it.next();
try {
if (localLOGV) Slog.v(TAG, "sending alarm " + alarm);
alarm.operation.send(mContext, 0,
mBackgroundIntent.putExtra(
Intent.EXTRA_ALARM_COUNT, alarm.count),
mResultReceiver, mHandler);
// we have an active broadcast so stay awake.
if (mBroadcastRefCount == 0) {
setWakelockWorkSource(alarm.operation);
mWakeLock.acquire();
}
final InFlight inflight = new InFlight(AlarmManagerService.this,
alarm.operation);
mInFlight.add(inflight);
mBroadcastRefCount++;
final BroadcastStats bs = inflight.mBroadcastStats;
bs.count++;
if (bs.nesting == 0) {
bs.nesting = 1;
bs.startTime = nowELAPSED;
} else {
bs.nesting++;
}
final FilterStats fs = inflight.mFilterStats;
fs.count++;
if (fs.nesting == 0) {
fs.nesting = 1;
fs.startTime = nowELAPSED;
} else {
fs.nesting++;
}
if (alarm.type == AlarmManager.ELAPSED_REALTIME_WAKEUP
|| alarm.type == AlarmManager.RTC_WAKEUP) {
bs.numWakeup++;
fs.numWakeup++;
ActivityManagerNative.noteWakeupAlarm(
alarm.operation);
}
} catch (PendingIntent.CanceledException e) {
if (alarm.repeatInterval > 0) {
// This IntentSender is no longer valid, but this
// is a repeating alarm, so toss the hoser.
remove(alarm.operation);
}
} catch (RuntimeException e) {
Slog.w(TAG, "Failure sending alarm.", e);
}
}
}
}
}
}
AlarmManagerService构造时,会启动一个AlarmThread。在这个AlarmThread中有一个while循环去执行waitForAlarm这个动作。
static void android_server_AlarmManagerService_set(JNIEnv* env, jobject obj, jint fd, jint type, jlong seconds, jlong nanoseconds)
{
struct timespec ts;
ts.tv_sec = seconds;
ts.tv_nsec = nanoseconds;
int result = ioctl(fd, ANDROID_ALARM_SET(type), &ts);
if (result < 0)
{
ALOGE("Unable to set alarm to %lld.%09lld: %s\n", seconds, nanoseconds, strerror(errno));
}
}
当alarm到期时,AlarmThread的waitForAlarm会返回一个值。接着通过执行 triggerAlarmsLocked,把几种类型的闹钟列表中符合要求的alarm添加到 triggerList中,然后用 alarm.operation.send发送消息。