一、App 定时提醒方案
1、Handler: 通过Handler的setMessageDelay()来实现定时发送消息的功能。但是Handler依赖于所在线程,线程结束就起不到定时效果。
2、Timer: 可以精确的做到定时操作,但是手机灭屏后长时间不使用,CPU就进入休眠模式。使用Timer定时就会失败 ,Timer无法唤醒CPU
3、AlarmManager: 是Android的全局定时器,就是指定时间做一件事情(封装在PendingIntent)。通过PendingIntent的getActivity()、getService()、getBroadcast()来执行具体。和Timer的区别是:Timer可能因为手机休眠而被杀掉,但是AlarmManager可以做到唤醒手机
二、AlarmManager
概述
AlarmManger提供了对系统定时服务的访问接口,使得开发者可以安排在未来某个时间运行应用。当到达闹钟设定时间,系统广播闹钟之前注册的Intent。如果此时应用还没有启动,系统还会帮你自动启动目标应用。即使设置进入睡眠,已注册的闹钟具有唤醒CPU的功能,可以保证每次需要执行特定的任务时CPU都能正常工作。唤醒CPU的功能是AlarmManager和Timer最大的区别。
闹钟的类型
RTC_WAKEUP: 使用System.currentTimeMillis(),能够唤醒设备
RTC: 使用Systent.currentTimeMillis(),不能够唤醒设备
ELAPSED_REALTIME:使用系统启动的时间,不能够唤醒设备。设备唤醒后发送出去
ELAPSED_REALTIME_WAKEUP:使用系统启动时间,能够唤醒设备。
一、分类
基于时间分类
1、基于绝对时间设置闹钟(系统当前时间System.currentTimeMillis())
2、基于相对时间设置闹钟(系统运行时长SystemClock.elapsedRealtime())
是否可以唤醒CPU
1、CPU在睡眠状态下,可唤醒设备(RTC_WAKEUP:绝对时间,ELAPSED_REALTIME_WAKEUP:相对时间)
2、不能唤醒设备,设备被唤醒后才能发出闹钟(RTC:绝对时间,ELAPSED_REALTIME:相对时间)
二、设置闹钟API
1、void set(int type,long triggerAtMillis,PendingIntent operation)
type: 类型
triggerAtMillis:定时时间
operation:定时执行的任务
add API 1
用于设置一次性的闹钟,在API 19之后为闹钟不准确。API小于19的设备是精准的。PS:因在API 19Android系统会将时间相近闹钟任务进行批量处理,将设备的"唤醒"次数降到最低,并将电池的使用降到最低。
2、void set(int type,long triggerAtMillis,AlarmManager.OnAlarmListener listener,Handler targetHandler)
type:类型
triggerAtMillis:定时时间
listener: 定时任务的回调
targetHandler: 任务回调线程的Handler,为null在主线程
add API 24
和方法1作用是一致的,唯一的区别是定时任务处理位置不同
总结:方法1和2在API19之后是不精准的,且在低功耗下不执行
3、void setExact(int type,long triggerAtMillis,PendingIntent operation)
type: 闹钟类型
triggerAtMillis:定时时间
operation: 定时执行的任务
add API 19
在规定的时间准时执行
官方提示:对于不必要的强烈需求精准的闹钟,没有必要使用此方法。该方法会降低系统的最小化电池使用能力。
4、void setExact(int type,long triggerAtMillis,String tag,AlarmManger.OnAlarmListener listener,Handler targetHandler)
type:闹钟类型
triggerAtMillis:定时时间
tag:监听器的Tag
listener:闹钟的定时任务回调
targetHandler:定时任务回调执行的线程的Handler,null在主线程中执行
add API 24
同方法3一致,回调结果位置不同。
5、void setAlarmClock(AlarmManager.AlarmClockInfo info,PendingIntent operation)
info: 闹钟参数封装类
operation: 定时任务的回调
add API 21
像方法3,但是type设置为RTC_WAKEUP
总结:方法3、4、5 是精准的。5支持在低功耗模式下也执行,3、4需要验证
6、void setInexactRepeating(int type,long triggerAtMillis,long intervalMillis,PendingIntent operation)
type: 闹钟类型
triggerAtMillis: 首次执行时间,不会执行之前的时间。若首次不执行,之后的重复定时任务也将不执行
intervalMillis:重复执行的间隔时间
operation: 定时任务的回调
add API 3
设置重复闹钟,不准确的。比方法7更省电。不会执行请求之前的定时任务,而且之后的任务都不会执行。虽然会重复执行,但执行间隔时间不同。
7、void setRepeating(int type,long triggerAtMillis,long intervalMillis,PendingIntent operation)
type: 闹钟类型
triggerAtMillis: 首次执行时间
intervalMillis:重复执行的间隔时间
operation: 定时任务的回调
add API 1
在API19之前是精准的。功能同方法6一致。
总结:方法6、7都是设置重复闹钟的,setRepeating更为精准。如果一个闹钟被延迟(比如系统休眠、非唤醒CPU类型的闹钟)。手机唤醒后会尽快回调一个闹钟,之后按照原间隔时间执行。例如你设置整点闹钟,然后手机在7:45到8:45在休眠,当8:45唤醒手机后会尽快回调一个闹钟,然后在9点在回调一个闹钟。
在API19之后所有的重复闹钟都是不精确的。
8、void setAndAllowWhileIdle(int type,long triggerAtMillis,PendingIntent operation)
type: 闹钟类型
triggerAtMillis: 执行时间
operation: 回调
add API 23(23之后才引入了低功耗和应用待机模式)
类似方法1,但是即使在系统处于低功耗模式下,也允许发出闹钟。这种类型的闹钟仅被用于必须在实际需要在低功耗时发出闹钟的场景。当闹钟发出后,该应用程序还会被系统添加到临时白名单中,持续大约10s中。以便该程序获取更多的唤醒锁,从而完成其工作。但是当设备处于低功耗模式下,该闹钟会严重影响设备的电量使用。系统为了减少滥用,对特定程序发出闹钟的频率进行了限制。在正常的系统下(非低功耗模式下)闹钟间隔时间不能小于1分钟,低于1分中,闹钟不会发出,在低功耗模式下,时间间隔不能小于15分钟。
像set()方法1一样,闹钟会被批量处理,也就是说闹钟不是精准的
9、void setExactAndAllowWhileIdle(int type,long triggerAtMillis,PendingIntent operation)
type: 闹钟类型
triggerAtMillis:执行时间
operation:回调
add API 23
类似setExact()(方法3),设置一次性精准闹钟。但它比方法8更精准。
总结: 允许在低功耗模式执行闹钟
注意:setAndAllowWhileIdle() 和 setExactAndAllowWhileIdle() 为每个应用触发闹钟的频率都不能超过每 9 分钟一次。
10、void setWindow(int type,long windowStartMillis,long windowLengthMillis,PendingIntent operation)
type: 闹钟类型
windowStartMillis: 闹钟发出的最早时间
windowLengthMillis: 闹钟需要在多久的是时间段内发出
operation: 闹钟的回调
add API 19
设置在某一个时间段内发出闹钟,对闹钟的及时性要求不高。它允许程序利用分批的方式来处理。这样即是对电池的优化,他对闹钟的准确性要求不高。
11、void setWindow(int type,long windowStartMillis,long windowLengthMillis,String tag,AlarmManager.OnAlarmListener listener,Handler targetHandler)
app API 24
同方法10一样,区别是闹钟的回调不同
总结: 设置在某个时间段内的闹钟,对准确性要求不高
12、void cancel(PendingIntent operation)
operation: 之前设置闹钟的Intent,这个参数必须不为null
add API 1
取消任何具有匹配意图的闹钟。任何类型的闹钟,只要其意图匹配都将被取消
13、void cancel(AlarmManager.Listener listener)
listener: 要移除的闹钟监听器
add API 24
取消任何发给该listener的闹钟
14、AlarmManager.AlarmClockInfo getNextAlarmClock()
AlarmManager.AlarmClockInfo: 下个即将到来的闹钟事件。如果没有,返回null
add API 21
获取下一个闹钟信息,所考虑的闹钟仅是通过setAlarmClock()设置的闹钟。
15、void setTime(long millis)
millis: 系统时间
add API 8
设置系统时间,需要Manifest.permission.SET_TIME权限
16、void setTimeZone(String timeZone)
timeZone:时区ID
add API 1
设置系统默认时区,需要Manifest.permission.SET_TIME_ZONE权限
三、常用的时间常量
public static final long INTERVAL_FIFTEEN_MINUTES = 15 * 60 * 1000;
public static final long INTERVAL_HALF_HOUR = 2*INTERVAL_FIFTEEN_MINUTES;
public static final long INTERVAL_HOUR = 2*INTERVAL_HALF_HOUR;
public static final long INTERVAL_HALF_DAY = 12*INTERVAL_HOUR;
public static final long INTERVAL_DAY = 2*INTERVAL_HALF_DAY;
四、使用同一个Intent,设置多个闹钟仅会有最后一个生效的问题
可以给每一个闹钟设置一个ID,保证id的唯一性就可以,这样取消时也可以根据id关闭闹钟。
五、不同SDK版本使用闹钟的注意事项
SDK API < 19
正常使用set() 、setRepeating()即可(19以下未添加闹钟批量处理、和系统低功耗模式)
SDK API >=19 && SDK API < 23
要求精准的闹钟,需要使用setExact()(19之后系统为了省电,添加闹钟批量处理,导致原设置的不精准)
重复闹钟,需要在闹钟回调中再次设置闹钟
SDK >= 23
Android 6.0映入了低功耗模式和应用待机模式,