Android|Android alarm解析

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); } } }

【Android|Android 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发送消息。



    推荐阅读