AlarmManager是Android中常用的一种系统级别的提示服务,在特定的时刻为我们广播一个指定的Intent,为你的应用设定一个在未来某个时间唤醒的功能。
当闹钟响起,实际上是系统发出了为这个闹钟注册的广播,会自动开启目标应用。
注册的闹钟在设备睡眠的时候仍然会保留,可以选择性地设置是否唤醒设备,但是当设备关机和重启后,闹钟将会被清除。
对于常规的短时间计时操作(ticks, timeouts, etc),使用Handler处理更加方便和有效率。
在alarm的receiver的onReceive()方法被执行的时候,AlarmManager持有一个CPU唤醒锁,这样就保证了设备在处理完广播之前不会sleep。一旦onReceive()方法返回,AlarmManager就会释放这个锁,表明一些情况下可能onReceive()方法一执行完设备就会sleep。如果你的alarmreceiver中调用了Context.startService(),那么很可能service还没起来设备就sleep了。为了阻止这种情况,你的BroadcastReceiver和Service需要实现不同的唤醒锁机制,来确保设备持续运行到service可用为止。
如果onReceive()方法里确实需要异步操作的话,可以用goAsync方法,然后在新开一个线程去执行。
如果onReceive()有过于耗时的操作,建议使用PendingIntent.getService()启动Service方式来完成。
BroadcastReceiver onReceive()使用goAsync 执行异步操作例子:
@Override
public void onReceive(final Context context, final Intent intent) {
final PendingResult result = goAsync();
final PowerManager.WakeLock wl = AlarmAlertWakeLock.createPartialWakeLock(context);
try {
wl.acquire();
} catch (Exception e) {
e.printStackTrace();
}
AsyncHandler.post(new Runnable() {
@Override
public void run() {
LogUtils.writeAlarmLog("AlarmStateManager received intent " + intent);
try {
handleIntent(context, intent);
result.finish();
wl.release();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
Alarm接口
1.cancel(PendingIntent operation)
Remove any alarms with a matching Intent.
2.changeAlarmType(String pkgName, boolean wakeup)
3.getNextAlarmClock()
Gets information about the next alarm clock currently scheduled.
4.set(int type, long triggerAtMillis, PendingIntent operation)
Schedule an alarm.
5.setAlarmClock(AlarmManager.AlarmClockInfo info, PendingIntent operation)
Schedule an alarm that represents an alarm clock.
6.setExact(int type, long triggerAtMillis, PendingIntent operation)
Schedule an alarm to be delivered precisely at the stated time.
7.setInexactRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation)
Schedule a repeating alarm that has inexact trigger time requirements; for example, an alarm that repeats every hour, but not necessarily at the top of every hour.
8.setRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation)
Schedule a repeating alarm.
9.setTime(long millis)
Set the system wall clock time.
10.setTimeZone(String timeZone)
Set the system default time zone.
11.setWindow(int type, long windowStartMillis, long windowLengthMillis, PendingIntent operation)
Schedule an alarm to be delivered within a given window of time.
常用接口
1.public void set/setExact(int type, long triggerAtMillis, PendingIntent operation)
该方法用于设置一次性闹钟。
第一个参数int type 指定定时服务的类型,该参数接受如下值:
ELAPSED_REALTIME: 在指定的延时过后,发送广播,但不唤醒设备(闹钟在睡眠状态下不可用)。如果在系统休眠时闹钟触发,它将不会被传递,直到下一次设备唤醒。
ELAPSED_REALTIME_WAKEUP: 在指定的延时过后,发送广播,并唤醒设备(即使关机也会执行operation所对应的组件)。延时是要把系统启动的时间SystemClock.elapsedRealtime()算进去的,具体用法看代码。
RTC: 指定当系统调用System.currentTimeMillis()方法返回的值与triggerAtTime相等时启动operation所对应的设备(在指定的时刻,发送广播,但不唤醒设备)。如果在系统休眠时闹钟触发,它将不会被传递,直到下一次设备唤醒(闹钟在睡眠状态下不可用)。
RTC_WAKEUP: 指定当系统调用System.currentTimeMillis()方法返回的值与triggerAtTime相等时启动operation所对应的设备(在指定的时刻,发送广播,并唤醒设备)。即使系统关机也会执行 operation所对应的组件。
第二个参数triggerAtMillis表示触发闹钟的时间。
第三个参数PendingIntent pi表示闹钟响应动作:
PendingIntent pi:是闹钟的执行动作,比如发送一个广播、给出提示等等。PendingIntent是Intent的封装类。需要注意的是:
启动服务:如果是通过启动服务来实现闹钟提示的话,PendingIntent对象的获取就应该采用Pending.getService(Context c,int i,Intent intent,int j)方法;
启动广播:如果是通过广播来实现闹钟提示的话,PendingIntent对象的获取就应该采用PendingIntent.getBroadcast(Context c,inti,Intent intent,int j)方法;
启动activity:如果是采用Activity的方式来实现闹钟提示的话,PendingIntent对象的获取就应该采用PendingIntent.getActivity(Context c,inti,Intent intent,int j)方法。
如果这三种方法错用了的话,虽然不会报错,但是看不到闹钟提示效果。
注意:AndroidL开始,设置的alarm的触发时间必须大于当前时间 5秒
AlarmManagerService中是通过PendingItent来标示一个Alarm的
2.public void cancel (PendingIntent operation)
移除intent相匹配的alarm(只要action,data, type,package,component,categories相等,就会被取消)。另外,应用被forcestoppackage杀掉之后,所有的alarm都会被移除。
3.public void setRepeating(int type,long startTime,long intervalTime,PendingIntent pi)
设置一个周期性执行的定时服务。第一个参数表示闹钟类型,第二个参数表示闹钟首次执行时间,第三个参数表示闹钟两次执行的间隔时间,第三个参数表示闹钟响应动作。
注意:该方法提供了设置周期闹钟的入口,闹钟执行时间严格按照startTime来处理,使用该方法需要的资源更多,不建议使用。
4.public void setInexactRepeating(int type,long startTime,long intervalTime,PendingIntent pi)
该方法也用于设置重复闹钟,与第二个方法相似,不过其两个闹钟执行的间隔时间不是固定的而已。它相对而言更省电(power-efficient)一些,因为系统可能会将几个差不多的闹钟合并为一个来执行,减少设备的唤醒次数。第三个参数intervalTime为闹钟间隔,内置的几个变量如下:
INTERVAL_DAY: 设置闹钟,间隔一天
INTERVAL_HALF_DAY: 设置闹钟,间隔半天
INTERVAL_FIFTEEN_MINUTES:设置闹钟,间隔15分钟
INTERVAL_HALF_HOUR: 设置闹钟,间隔半个小时
INTERVAL_HOUR: 设置闹钟,间隔一个小时
AndroidL开始repeat的周期必须大于60秒
设置一个闹钟和取消一个闹钟的例子:
private static class AlarmManagerStateChangeScheduler implements StateChangeScheduler {
@Override
public void scheduleInstanceStateChange(Context context, Calendar time,
AlarmInstance instance, int newState) {
long timeInMillis = time.getTimeInMillis();
Intent stateChangeIntent = createStateChangeIntent(context, ALARM_MANAGER_TAG, instance,
newState, VALUE_CHANGE_STATE_BY_SCHEDULE);
// Treat alarm state change as high priority, use foreground broadcasts
stateChangeIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
PendingIntent pendingIntent = PendingIntent.getService(context, instance.hashCode(),
stateChangeIntent, PendingIntent.FLAG_UPDATE_CURRENT);
//设置一个闹钟
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (CommonUtils.isMOrLater()) {
am.setExactAndAllowWhileIdle(ReflectUtils.getRTC_BOOT_WAKEUP()/*AlarmManager.RTC_WAKEUP*/, timeInMillis, pendingIntent);
LogUtils.writeAlarmLog("Scheduling state change success by setExactAndAllowWhileIdle");
} else if (CommonUtils.isKitKatOrLater()) {
am.setExact(ReflectUtils.getRTC_BOOT_WAKEUP()/*AlarmManager.RTC_WAKEUP*/, timeInMillis, pendingIntent);
LogUtils.writeAlarmLog("Scheduling state change success by setExact");
} else {
am.set(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);
LogUtils.writeAlarmLog("Scheduling state change success not KitKatOrLater");
}
}
@Override
public void cancelScheduledInstanceStateChange(Context context, AlarmInstance instance) {
LogUtils.writeAlarmLog("cancelScheduledInstance instance id = " + instance.mId);
// Create a PendingIntent that will match any one set for this instance
PendingIntent pendingIntent = PendingIntent.getService(context, instance.hashCode(),
createStateChangeIntent(context, ALARM_MANAGER_TAG, instance, null, null),
PendingIntent.FLAG_NO_CREATE);
//取消一个闹钟
if (pendingIntent != null) {
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
am.cancel(pendingIntent);
pendingIntent.cancel();
ReflectUtils.cancelOnePoweroffAlarm(am, pendingIntent);
}
}
}