AlarmManagerService讲解

1.定义

  该服务在SystemServer的其他服务中启动,是Android中系统级别的提醒服务,其主要作用是在某一个特定的时候能够唤醒设备并执行一定的操作,但是当系统关机或者重启之后则会被清除。在不同的版本上面Google做了很多功耗处理,在Android4.4以上alarm机制是非准确传递的,来最小化唤醒和电池使用,也就是使用set()和setRepeating()方法设置的闹钟则会变得不准确(Android现在批处理在合理的相似时间发生的所有应用的闹钟,以便系统仅唤醒设备一次),如果一定要准确的传递,那么可使用提供的setExact()函数;但在Android6.0以上的低电耗模式下(用户拔下充电插头并在屏幕关闭后的一段时间内使其保持不活动状态)标准的AlarmManager闹钟(包括setExact()和setWindow())将会被推迟到下一个维护阶段,如果需要在低电耗模式下触发闹钟,则使用setExactAndAllowWileIdle()或者setAndAllowWhileIdle()。系统中通过AlarmManager来管理所有的Alarm。

2.AlarmManager讲解

2.1 定义

  该类中的所有实现都会调用到AlarmManagerService中,也就是外部访问AlarmManagerService的中间类。该类在SystemServiceRegistry中通过静态代码块的方式将对应的Service名称以及CachedServiceFetcher对象(用于生成对应Manager的对象)保存在HashMap中,当用户通过ContextImpl中的getSystemService()函数调用到SystemServiceRegistry中的getSystemService()函数之后,在该函数中会调用到CachedServiceFetcher对象中的getService()函数,并通过该函数生成对应的Manager实例,时序图如下图所示:

2.2 flag讲解

(1)FLAG_STANDALONE = 1;用于标识该alarm不会被加入到其他alarm集合中去(在Android4.4以上是非准确传递的,对时间相近的alarm会进行批处理),单独进行处理;

(2)FLAG_WAKE_FROM_IDLE = 2;用于标识设备即使处于idle状态,也会被唤醒处理alarm;

(3)FLAG_ALLOW_WHILE_IDLE = 4;用于标识设备即使处于idle状态也会处理alarm,并且设备不会退出idle状态;

(4)FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED = 8;类似于3,但是它的运行不会受到任何约束,仅适用于系统alarm;

(5)FLAG_IDLE_UNTIL = 16;仅适用于系统,用于告诉AlarmManager在什么时候退出idle模式,也就是只能在DeviceIdleController使用,当DeviceIdleController设置该flag的时候,说明系统已经进入到了idle状态;

(6)RTC_WAKEUP = 0;通过System.currentTimeMillis()进行时间设置,并且设备在进入到idle状态的时候设备会被唤醒;

(7)RTC = 1;通过System.currentTimeMillis()进行时间设置,并且设备在进入到idle状态的时候不会被执行;

(8)ELAPSED_REALTIME_WAKEUP = 2;通过SystemClock.elapsedRealtime()进行时间设置,并且设备在进入到idle状态的时候会被执行;

(9)ELAPSED_REALTIME = 3;通过SystemClock.elapsedRealtime()进行时间设置,并且设备在进入到idle状态的时候不会被执行。

3.AlarmManagerService讲解

3.1 通过使用AlarmManager启动Alarm首先会进入到AMS中的IBinder类中的set()函数之后,进行了如下判断操作:

(1)通过调用Alarm的应用uid判断该应用类型以此来初始化flag(用于标识该应用的alarm是否属于AlarmManager中定义的四种类型来表示alarm是否需要被特殊处理)的值;

(2) 判断传入数据的合法性:如果设置了windowLength(setWindow函数启动Alarm,用于设置Alarm可被延迟的时间)参数,如果大于了12个小时则设置为一个小时,循环唤醒的间隔时间最大值为365天最小值为1分钟,如果超过了最大值最小值的限定,则赋值为最大值或者最小值。

3.2 将绝对时间转换为相对时间进行Alarm的触发,并计算最大延迟Alarm触发时间,代码如下:

    //获取从开机到当前的时间
    final long nowElapsed = SystemClock.elapsedRealtime();
    //如果type是RTCxx类型,则triggerAtTime -= System.currentTimeMillis() - SystemClock.elapsedRealtime();
    //也就是转换为开机时间进行处理
    final long nominalTrigger = convertToElapsed(triggerAtTime, type);
    //最小触发时间=开机时间+5秒
    final long minTrigger = nowElapsed + mConstants.MIN_FUTURITY;
    //为防止alarm频繁滥发执行,最小触发Alarm的时间为5秒间隔
    final long triggerElapsed = (nominalTrigger > minTrigger) ? nominalTrigger : minTrigger;
    //用于计算最大触发Alarm的时间延时
    final long maxElapsed;
    if (windowLength == AlarmManager.WINDOW_EXACT) {
        //如果alarm的时间窗为0,则表示为精确执行时间
        maxElapsed = triggerElapsed;
    } else if (windowLength < 0) {
        //最晚触发时间
        maxElapsed = maxTriggerTime(nowElapsed, triggerElapsed, interval);
        windowLength = maxElapsed - triggerElapsed;
    } else {
        //如果时间窗口的时间大于0,则把最早触发时间+窗口时间设置为最晚触发时间
        maxElapsed = triggerElapsed + windowLength;
    }   
复制代码

3.3 判断当前设置的alarm是否设置了FLAG_IDLE_UNTIL标签,代码如下:

 //如果设置了FLAG_IDLE_UNTIL
 if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) {
        //判断最早可将系统从idle状态唤醒的alarm执行时间是否小于当前所设置的alarm执行时间,如果
        //是则对当前alarm执行时间进行改变
        if (mNextWakeFromIdle != null && a.whenElapsed > mNextWakeFromIdle.whenElapsed) {
            a.when = a.whenElapsed = a.maxWhenElapsed = mNextWakeFromIdle.whenElapsed;
        }
        final long nowElapsed = SystemClock.elapsedRealtime();
        //根据当前alarm延迟时间fuzz存在如下几个可能值:
        //(1)delay < 15min,return delay;
        //(2)delay < 90min,return 15min;
        //(3)else return 30min
        final int fuzz = fuzzForDuration(a.whenElapsed-nowElapsed);
        //根据生成的fuzz产生0-fuzz范围内的随机时间并赋值
        if (fuzz > 0) {
            if (mRandom == null) {
                mRandom = new Random();
            }
            final int delta = mRandom.nextInt(fuzz);
            a.whenElapsed -= delta;
            a.when = a.maxWhenElapsed = a.whenElapsed;
        }
    //如果在之前已经设置了FLAG_IDLE_UNTIL该标签的alarm,并且当前设置的alarm不存在如下几种标签,但是又需要在idle状态运
    //行,那么就添加到mPendingWhileIdleAlarms延迟开始执行列表中
    } else if (mPendingIdleUntil != null) {
        if ((a.flags&(AlarmManager.FLAG_ALLOW_WHILE_IDLE | AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED |              AlarmManager.FLAG_WAKE_FROM_IDLE))
                == 0) {
            mPendingWhileIdleAlarms.add(a);
            return;
        }
    }
复制代码

3.4 根据调用alarm应用的应用待机群组类别进行重新调整设置alarm的触发时间。关键代码如下:

 //获取应用standbyBucket类别
 final int standbyBucket = mUsageStatsManagerInternal.getAppStandbyBucket(
            sourcePackage, sourceUserId, SystemClock.elapsedRealtime());
    final Pair<String, Integer> packageUser = Pair.create(sourcePackage, sourceUserId);
    //获取上一次发送alarm的时间
    final long lastElapsed = mLastAlarmDeliveredForPackage.getOrDefault(packageUser, 0L);
    if (lastElapsed > 0) {
        //下一次alarm最小触发时间
        final long minElapsed = lastElapsed + getMinDelayForBucketLocked(standbyBucket);
        if (alarm.expectedWhenElapsed < minElapsed) {
            alarm.whenElapsed = alarm.maxWhenElapsed = minElapse
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值