言归正传! 在最近的Bug中发现一个问题,项目有用到预定的功能,因此就使用了AlarmManager来进行定时提醒的功能。当时开发这个功能的时候并无发现会有这种Bug,缘由是开发时只会预定一个来测试功能是否有实现,实现了就算完成,而这个问题刚好是有多个预定时才会发生的问题(这里的多个预定是指系统中有多个AlarmManager的定时,具体为何这样说,下面我会提到),好比当我进行了10个预定,也就是设定了10个AlarmManager的时候,我让设备休眠(我用的Type是RTC_WAKEUP),依次观察10个通知的提醒。发现其中会有一两个通知并无按照我设定的时间唤醒设备,而是会和与他时间相近的下一个预定同时出现(也就是发生了时间不精确的问题)。当时发现这个问题时觉得是我在传递预定时间时发生了问题,就打Log查看了一下,发现并无什么问题,既然时间没有问题,那颇有多是AlarmManager的问题,上网查了一下,在Android的文档中发现了端倪。android
官方文档说从API19(android4.4)开始, 为了节能省电(减小系统唤醒和电池使用)。使用Alarm.set()和Alarm.setRepeating()已经不保证精确性(是否是忽然一下就蒙了,不精确还怎么用?),不过Google怎么可能会作出这样不合理的设计呢!接着看就会发现其实Google还提供两个精确的Alarm方法,setWindow()和setExact(),看来问题是解决了。那顺便也分析一下AlarmManager吧,分析的过程当中发现使用setAlarmClock这个方法也能够实现精准通知,可是有局限性,下面会说到。测试
浏览了一下官方文档和AlarmManager这个类,发现其实这个类很简单,就是定义了几个Type变量和set、cancle方法(其实AlarmManager的核心在AlarmManagerService中,这个一下子再说)。这里只介绍一些常用的。设计
常用的变量:
其实主要就是分为两类,系统相对时间和绝对时间和是否唤醒CPU。
ELAPSED_REALTIME:使用相对时间,能够经过SystemClock.elapsedRealtime() 获取(从开机到如今的毫秒数,包括手机的睡眠时间),设备休眠时并不会唤醒设备。
ELAPSED_REALTIME_WAKEUP:与ELAPSED_REALTIME基本功能同样,只是会在设备休眠时唤醒设备。
RTC:使用绝对时间,能够经过 System.currentTimeMillis()获取,设备休眠时并不会唤醒设备。
RTC_WAKEUP: 与RTC基本功能同样,只是会在设备休眠时唤醒设备。
还有其余几个变量是针对API19和特殊方法使用的,这里就不具体介绍,能够经过文档查看。
常用的方法:
对于方法我不会把每个都讲一遍,应为全部的set方法其实都是调用内部的一个private的方法setImpl(),只是不一样的set方法传入的值不一样而已,我就说一下setImpl这个方法。对象
能够看到setImpl这个方法内部也很简单,就是判断一下triggerAtMillis这个参数,而后调用mService.set方法。那这个mService又是什么呢?blog
在上面找发现了mService是一个叫IAlarmManager的service,这里变量是红色的,我用AndroidStudio没法再跟踪下去,可是看到这儿咱们应该就想到安卓的AIDL通讯方式,其实AlarmManager就是用的AIDl通讯,从Alarm对象的获取咱们也能够看出来排序
AlarmManager am = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
是获取的系统服务,AlarmManager真正的实现都在FrameWork层的AlarmManagerService这个服务中。(从AlarmManager的构造方法中咱们也能够看到他进行了API19版本的判断,这里咱们还要注意mAlwaysExact这个参数,下面会用到)我先将setImpl的几个参数的含义说一下:
Type:就是咱们上面说的那些变量。
triggerAtMillis:alarm的触发时间。
windowMillis:这个参数很繁琐,只有setWindow会主动设置,它的意思是设置一个时间区间,他会在triggerAtMillis的时间开始的这个区间内触发Alarm通知。普通的set()和setRepeating()方法会经过调用legacyExactLength()这个方法返回这个值:接口
这个方法就用到了咱们上面说的mAlwaysExact这个变量,若是是小于API19的版本会使用
WINDOW_EXACT参数,这个参数是0(意思就是区间设置为0,那么就会按照triggerAtMillis这个时间准时触发,也就是精准触发)另外一个参数WINDOW_HEURISTIC的值是-1,这个值具体的用法就要看AlarmManagerService具体的实现了,反正只要知道这个值是不精准就能够。剩下的其余set方法直接就将这个值设置为WINDOW_EXACT,那么那几个方法就都是精准通知了,除了setInexactRepeating方法将参数设置为WINDOW_HEURISTIC。开发
intervalMillis:间隔时间,用到Repeating类型的通知时使用,效果就相似咱们设置的闹钟,过10分钟后提醒一次同样。
operation: 就是PendingIntent,没什么说的。
worksource:这个参数我也不知道什么意思,应为全部的方法都设置它为null,有知道的朋友能够告诉我一下。
alarmClock:设置AlarmClockInfo对象,看代码里这个对象实现了Parcelable接口,其实就是对triggerTime和PendingIntent的一个封装类。文档
那么参数就基本说完了,你们只须要不一样方法传递不一样参数就能够了。再说一下setAlarmClock这个方法,应为它用的是WINDOW_EXACT这个参数,因此这个方法也是精准的,不过他有一些局限性:
1:他只能在API21版本和以后的版本调用。
2:他的Type是固定的RTC_WAKEUP。get
因为好奇心我又在网上查找了一下AlarmManagerService节能省电的原理是什么?经过看不少大神的分析,了解了原理,这里我简单总结一下。若是咱们没有设置和使用精准通知的话,系统会把触发时间相近的Alarm放在同一个batch(看名字是一批的意思)中,而后每一个bach根据时间排序放在mAlarmBatchs中,前面的就是先要触发的alarm。这就是原理, 也就是咱们的Alarm会分为一批一批的一块儿触发,而不是每一个Alarm都要触发。这就是我上面说的系统中有多个Alarm时会发生时间不许的现象,但若是系统中只有几个Alarm,而且他们的触发时间隔得很远,那么咱们的Alarm就本身分为一批,触发仍是会准的。 好了,对于AlarmManager的分析就到这里。有不对的地方但愿大牛指导。