1.AlarmManager作用
AlarmManager提供了访问系统闹钟的服务。它允许你安排你的应用在未来某个时间点运行。这就相当于定时任务。对于少于60s的定时任务,不推荐使用AlarmManager,可以使用更高效的的Handler来处理频发的任务。
2.AlarmManager设置定时任务方法解析
1)set(int type, long triggerAtMillis, PendingIntent operation)
该方法用于设置一次性闹钟。type表示闹钟类型。triggerAtMillis表示闹钟的触发时间。Operation表示要执行的操作。如果已经存在相同的闹铃安排要执行的Intent(也就是PendingIntent)也相同,那么前一个被设置的闹铃会被取消。如果定义闹铃的触发时间在过去,那么闹铃会立即被触发。如果已经有了这个意图的(通过Intent.filterEquals定义两个意图的相等性),那么旧的意图将被删除并替换为当前设置的意图。
从API 19开始,闹铃的触发时间已经不精确了,闹铃不会在设定的触发时间之前被触发,但是有可能会出现闹铃延迟触发的现象。这是为了节能省电(减少系统唤醒和电池使用)。一般情况下,如果设置的触发时间是在将来,而且时间不久,闹铃一般是不会被延迟。但只要闹铃设置的触发时间比较长就很有可能会被推迟触发。
在新的批处理策略下,如果应用程序设置了多个闹铃,那么这些闹铃的实际触发顺序可能与它们所请求触发时间的顺序不匹配。如果你的应用程序有强烈的排序需求,那么你可以使用setWindow(int, long, long, PendingIntent) and setExact(int, long, PendingIntent)来实现。
2)setRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation)
该方法用于设置重复闹钟。type表示闹钟类型,triggerAtMillis表示闹钟的触发时间。intervalMillis表示闹钟两次执行的间隔时间,operation表示闹钟执行的操作。
当调用这个方法设置闹铃的时候,这个闹铃会按照设定的时间间隔不断触发,直到用cancel显式地取消这个闹铃。
如果定义的触发时间在过去,则会立即触发警报,它会根据触发时间相对于重复间隔的时间间隔,触发闹铃计数。
如果一个闹铃被延迟了(例如,对于非唤醒类型的闹铃遇到系统休眠的时候),一个已经过时的的闹铃会尽快被触发。在此之后,未来的闹铃将按原计划被触发;他们不会随着时间的推移而改变。例如,如果你在每小时都设置了一个重复的闹钟,但是手机在7:45到8:45之前已经就已经休眠了,一旦手机唤醒,就会发出警报,然后下一个闹钟将在9点被触发。
在API 19中,所有的重复闹铃都是不准确的。如果你的应用程序需要精确的触发时间,那么它必须使用一次性的警报,当闹铃被触发后重新再重新安排一个相同时间间隔的闹铃。
3) setInexactRepeating(int type, long triggerAtMillis,long intervalMillis, PendingIntent operation)
该方法也用于设置重复闹钟。type表示闹钟类型,triggerAtMillis表示闹钟的触发时间。intervalMillis表示闹钟两次执行的间隔时间,operation表示闹钟执行的操作。这个方法和setRepeating差不多,但是它是不准确的,它主要用于对触发时间要求不严格的重复任务。相对于setRepeating方法,也更加节能。因为系统会将差不多的闹钟进行合并,以避免在不必要地唤醒设备。
在API 19中,所有的重复警报都是不准确的。
你的闹铃的第一个触发器将不会在请求的时间之前被触发,但是在此之后可能不会出现一个完整的时间间隔。此外,尽管重复报警的总周期是按要求进行的,但两次连续两次的警报之间的间隔时间可能会有所不同。如果你的应用程序要求时间间隔比较严格,那么可以使用一个适当的窗口来调用一个闹铃; setWindow(int, long, long, PendingIntent) 和 setExact(int, long, PendingIntent).
4) setWindow(int type, long windowStartMillis, long windowLengthMillis,
PendingIntent operation)
设置一个闹钟在给定的时间窗触发。类似于set,该方法允许应用程序精确地控制操作系统调整闹钟触发时间的程度。
这里的时间窗是什么意思?带着疑问到网络上找了一下,貌似时间窗这个概念是股票领域一个叫江恩的人提出的,网上解释说时间窗口有两个含义:
一是指时间周期运行到一定的阶段时,便会发生转折;
二是指在时间周期中的某一关键时间段,如果对特定事物施加影响或采取某种行动,成功几率将增加。
一开始我认为这是一个时间区间的概念,也就是说闹铃会在[windowStartMillis, windowLengthMillis]区间内的某个时间触发。但是多次测试发现触发时间会出现在区间之外。所以我也弄不清楚windowStartMillis和windowLengthMillis的作用了,可以确定的是闹铃触发时间肯定是在windowStartMillis之后的。有清楚的小伙伴麻烦说一下。
type表示闹钟类型,windowStartMillis表示时间窗的开始点。windowLengthMillis表示时间窗的长度,operation表示闹钟执行的操作。
该方法还可用于在多个闹铃中实现严格的排序保证,确保每个闹铃的请求不交叉。
当不需要精确的触发时,应用程序应该使用set(int,long,PendingIntent)方法。这将给操作系统最大的灵活性,以最小化唤醒系统和电池使用。对于必须在精确指定的时间触发闹铃,应用程序可以使用setExact(int,long,PendingIntent)。
我们需要注意的是setWindow方法是在API>19后才能调用的。
5) setExact(int type, long triggerAtMillis, PendingIntent operation)
设置一个闹钟在给定的时间内准确的触发。这种方法就像set(int, long, PendingIntent),但不允许操作系统调整触发时间。闹铃将尽可能接近给定的触发时间点触发。type表示闹钟类型,triggerAtMillis表示闹钟的触发时间。operation表示闹钟执行的操作。
只有当迫切需要在闹铃在准确的时间内触发才调用这个方法。但是不鼓励应用程序调用准确的闹铃,因为他们减少了操作系统的运算能力和耗电。
这个方法是在API>19后才能调用的。
6)设置闹铃的方法中的type
AlarmManager.RTC,该状态下闹钟使用绝对时间,即当前系统时间,不唤醒手机(也可能是其它设备)休眠;当手机休眠时不触发闹钟。
AlarmManager.RTC_WAKEUP,该状态下闹钟使用绝对时间,即当前系统时间,当闹钟发躰时唤醒休眠状态的手机;
AlarmManager.ELAPSED_REALTIME,该状态下闹钟使用相对时间(相对于系统启动开始),不唤醒手机休眠;当手机休眠时不触发闹钟。
AlarmManager.ELAPSED_REALTIME_WAKEUP,该状态下闹钟使用相对时间(相对于系统启动开始),当闹钟触发时唤醒休眠状态的手机;
RTC闹钟和ELAPSED_REALTIME最大的差别就是前者可以通过修改手机时间触发闹钟事件,后者要通过真实时间的流逝,即使在休眠状态,时间也会被计算。
7)其他
Android 6.0最大变化之一就是加入了新的电量管理模式:休眠模式,当设备一段时间不用的时候,当屏幕关闭的时候,系统会自动进入休眠模式。这样所有的App都将进入挂起模式,不能进行接入网络等一些操作。
当然系统也会定期的退出休眠模式,来完成App延迟的工作,在这个空窗期(我暂且就这么叫),系统会运行所有同步,工作,提醒等,并允许app接入网络。
当过了空窗期后,系统会重新进入休眠期,App也会随着挂起状态,随着时间的推移,空窗期越来越少,是为了帮助没有接入充电器的长期闲置的设备减少电池消耗。
只要用户唤醒设备,打开屏幕或者接入电源,系统会自动退出休眠模式,所有的活动都会恢复正常状态。
下面是当进入休眠期时的约束:
- 延迟网络请求;
- 系统忽略唤醒锁;
- 标准的闹钟提醒(包括setExat()和setWindow())会被延时到下一个空窗期;
如果一定要在休眠期唤醒闹钟,可以用setAndAllowWhileIdle()唤醒不精确闹钟或者setExactAndAllowWhileIdle()唤醒精确闹钟
闹钟设置setAlarmClock()则继续保持常态,在唤醒这个闹钟前系统会退出休眠期一段时间; - 禁用wifi scan;
- 不允许同步;
- 禁用JobScheduler ;
休眠容易影响 AlarmManager alarms and timers manage,因为当系统进入休眠状态,闹钟在android5.1(API level 22)或者更低不会唤醒 ;
为了帮助管理alarms,android 6.0(API level 23) 介绍了两个方法: setAndAllowWhileIdle()和setExactAndAllowWhileIdle(). 这样即使你再休眠期的时候 闹钟也会被唤醒;
PS: 即使这两种方法,每个App每15分钟唤醒次数也不能超过一次;
有了休眠,自然会影响我们的后台服务,比如 推送,google 建议是用GMC( Google Cloud Messaging)。
3.代码解析(关于使用AlarmManager实现定时任务的例子)
首先,初始化一个闹铃
//这个方法我是在Acitivity中定义的,这里举例设置闹铃用的是set方法
private void initAlarmManager()
{
//创建Intent对象,action为ELITOR_CLOCK,附加信息为字符串“点击我了”
Intent intent = new Intent("ELITOR_CLOCK");
intent.putExtra("msg","点击我了");
//定义一个PendingIntent对象,PendingIntent.getBroadcast包含了sendBroadcast的动作。
//PendingIntent不仅可以发给广播,还可以是
//getActivity(context, requestCode, intent, flags)
//getActivities(context, requestCode, intents, flags)
//getService(context, requestCode, intent, flags)
//只需要AndroidMenifest.xml中注册的组件里的意图过滤器中添加对应的action标签即可。
//也就是发送了action 为"ELITOR_CLOCK"的intent
PendingIntent pi = PendingIntent.getBroadcast(this,0,intent,0);
//AlarmManager对象,注意这里并不是new一个对象,Alarmmanager为系统级服务
AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
//设置闹钟从当前时间开始,每隔5s执行一次PendingIntent对象pi,注意第一个参数与第二个参数的关系
// 5秒后通过PendingIntent pi对象发送广播
am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,5*1000,pi);
}
然后,我们需要定义一个广播用来处理闹铃被触发后要处理的事务
public class MyReceiver extends BroadcastReceiver {
private static final int NOTIFICATION_FLAG = 1;
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
LogUtil.init("TestAlarmManager", "alarmLog.txt");
//这里获取的是上面闹铃传递的值“点击我了”
String msg = intent.getStringExtra("msg");
Log.i("lgy", "onclock......................" + msg);
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
}
}
定义了广播之后别忘了注册广播
注册广播4.源码地址
http://download.csdn.net/download/lgywsdy/9995903
5.参考文章
https://plus.google.com/+AndroidDevelopers/posts/GdNrQciPwqo
网友评论