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


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<Alarm> triggerList = new ArrayList<Alarm>();
                
                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<Alarm> 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发送消息。




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值