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




更多相关文章

  1. Android屏幕待机时间的获取和设置
  2. Android(安卓)- SQLite
  3. Android(安卓)ADB 工具使用
  4. Android(安卓)ShareSDK第三方登录(分别有新浪、QQ、微信、Faceboo
  5. android选择时间攻略
  6. android中数字及模拟小时钟设计
  7. Android(安卓)如何对/dev/log路径设备节点进行读写
  8. Android(安卓)Jetpack 系列篇(二) WorkManager
  9. Android.jar文件分析

随机推荐

  1. 2011.04.14——— android 可伸缩的listv
  2. Android 实用开源项目集合 持续更新
  3. findViewById()
  4. android修改Zxing源码,避免出现错误信息
  5. Android联系人数据库全解析(3)
  6. Android 源码中编译Android studio工程
  7. Android音频播放
  8. [干货] Android 深入浅出 Activity 生命
  9. android 的 service远程运用
  10. Android(安卓)Studio老是提示重启ADB解决