Android知识点 120 —— AlarmManager

目录

1 概述

2 为什么要有AlarmManager

3 闹铃类型

4 AlarmManager的常用方法

4.1 设置时间

4.2 设置闹铃

4.2.1 set

4.2.2 setExact

4.3 取消闹铃

4.4 获得下一次闹铃事件

5 常用时间定义

6 举个例子操作一下

6.1 初始化PendingIntent

6.2 初始化AlarmManger


1 概述

        AlarmManager类提供对系统警报服务的访问。这些允许您安排应用程序在将来的某个时间运行。当警报响起时,Intent系统会广播为其注册的警报,如果目标应用程序尚未运行,则会自动启动它。设备处于休眠状态时会保留已注册的警报(如果设备在此期间关闭,则可以选择将设备唤醒),但如果设备关闭并重新启动,则会清除AlarmManager的任务。
        AlarmManager 系统提供的一个定时任务管理器,通过AlarmManager 提供的定时任务,可以在约定的时间发送广播启动服务启动Activity等等(如何实现看后面的例子第6部分)。AlarmManager是Android中常用的一种系统级别的提示服务,在特定的时刻为我们广播某个指定的Intent。简单的说就是我们设定某一个时间,然后在该时间到来时,AlarmManager为 我们广播一个我们设定的Intent广播因此我们需要实现一个针对特定闹钟事件的广播接收器 (PendingIntent)。

 

2 为什么要有AlarmManager

比较Timer、Hnadler、AlarmManager

Timer,有一个明显的问题,它并不太适合用于需要长期在后台运行的定时任务。我们都知道,为了能让电池更加耐用,每种手机都会有自己的休眠策略,Android手机就会在长时间不操作的情况下自动让CPU进入睡眠状态,这就有可能导致Timer中的定时任务无法正常运行。

Hnadler,Handler的postDelay方法也可以实现定时操作,它同样也是不靠谱的,因为默认Hnadler依赖于线程(main线程或者子线程),所以只要进程被杀死,所有相关的线程都被晒死,所以handler中的定时操作就无效了。Timer也一样,因为Timer实际上是另起一个子线程,进程被杀,子线程当然也被杀了。

AlarmManager,它通过pendingIntent具有唤醒未启动进程的功能,即可以保证每次需要执行定时任务的时候CPU都能正常工作。但是当设备关机和重启后,闹钟将被清除。

 

3 闹铃类型

AlarmManager中一共提供了四种闹钟类型,

前两种对应的System.currentTimeMillis()(系统当前时间,或者叫绝对时间。例如 2020年几月几日。实际上这个时间的表达格式为当前计算机时间和GMT时间(格林威治时间)1970年1月1号0时0分0秒所差的毫秒数)时间,

后两种对应SystemClock.elapsedRealtime()(系统运行时间,或者叫相对时间。是从系统启动开始之后计时的)时间,

以WAKEUP结尾的类型能够唤醒设备,其他的类型不能唤醒设备,直到设备被唤醒才能出发警报提醒。

    public static final int RTC_WAKEUP = 0;
    public static final int RTC = 1;
    public static final int ELAPSED_REALTIME_WAKEUP = 2;
    public static final int ELAPSED_REALTIME = 3;

闹钟类型:一般为 AlarmManager.ELAPSED_REALTIME_WAKEUP 或者  AlarmManager.RTC_WAKEUP。它们之间的区别就是前者是从手机开机后的时间,包含了手机睡眠时间;而后者使用的就是手机系统设置中的时间。所以如果设置为AlarmManager.RTC_WAKEUP ,那么可以通过修改手机系统的时间来提前触发定时事件。另外,对于相似的 AlarmManager.ELAPSED_REALTIMEAlarmManager.RTC 来说,它们不会唤醒 CPU 。所以使用的频率较少;

1)这里补充一句,这些带WakeUp类型的闹钟,同样的在开机的情况下,也是会生效的,也是会触发闹钟的。并不是说必须待机后,才会触发wakeup类型的闹钟。这点要注意。

2)交流断电后,时钟是否还会生效? 应该是不会了。需要重新设置时钟。在哪里设置呢?考虑收到Boot Completed广播后重新去设置时钟。

这里肯定不会生效了,请看 AlarmManager.java 的文件头部的说明。

Registered alarms are retained while the device is asleep [...] but will be 
cleared if it is turned off and rebooted.

3)挺佩服有些手机没电了关机,充电后,未开机的情况下,还是可以闹钟工作。看样子是设置到了外部时钟里了。单独供电的外部时钟还可以唤醒设备?

4)还有一个方案,静默开机,充电后自动开机,然后再进入待机。走2)就实现了。

 

4 AlarmManager的常用方法

4.1 设置时间

在AlarmMananger中提供了setTime和setTimeZone方法分别用来设置系统时间和系统默认时区。其中,设置系统时间需要"android.permission.SET_TIME"权限。

返回值公开方法
voidsetTime(long millis)
voidsetTimeZone(String timeZone)

4.2 设置闹铃

返回值公开方法
voidset(int type, long triggerAtMillis, PendingIntent operation)
voidset(int type, long triggerAtMillis, String tag, AlarmManager.OnAlarmListener listener, Handler targetHandler)
voidsetAlarmClock(AlarmManager.AlarmClockInfo info, PendingIntent operation)
voidsetAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation)
voidsetExact(int type, long triggerAtMillis, PendingIntent operation)
voidsetExact(int type, long triggerAtMillis, String tag, AlarmManager.OnAlarmListener listener, Handler targetHandler)
voidsetExactAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation)
voidsetInexactRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation)
voidsetRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation)
voidsetWindow(int type, long windowStartMillis, long windowLengthMillis, PendingIntent operation)
voidsetWindow(int type, long windowStartMillis, long windowLengthMillis, String tag, AlarmManager.OnAlarmListener listener, Handler targetHandler)

以上是AlarmManager中提供的所有设置闹铃的方法,下面来详细介绍一下

  • 4.2.1 set

返回值公开方法
voidset(int type, long triggerAtMillis, PendingIntent operation)
voidset(int type, long triggerAtMillis, String tag, AlarmManager.OnAlarmListener listener, Handler targetHandler)

用于设置一次性闹铃,执行时间在设置时间附近,为非精确闹铃。方法一和方法二的区别:到达设定时间时方法一会广播PendingIntent中设定的Intent,而方法二会直接回调OnAlarmListener 中的onAlarm()方法。

还是举个栗子吧。以下例子来自《疯狂Android讲义》 10.7.2 设置闹钟 章节

Intent intent = new Intent();
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES |
            Intent.FLAG_RECEIVER_FOREGROUND);

intent.setAction("self.intent.rtctimer.wakeup");

intent.putExtra("name", "xiaoming");
intent.putExtra("age", 18);

PendingIntent pi = PendingIntent.getBroadcast(getApplicationContext(), 11, intent,
                      PendingIntent.FLAG_CANCEL_CURRENT);

Log.i(TAG, "set timer, config is xiaoming and 18");

alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMills() + 120, pi);

 

  • 4.2.2 setExact

返回值公开方法
voidsetExact(int type, long triggerAtMillis, PendingIntent operation)
voidsetExact(int type, long triggerAtMillis, String tag, AlarmManager.OnAlarmListener listener, Handler targetHandler)
voidsetAlarmClock(AlarmManager.AlarmClockInfo info, PendingIntent operation)

用于设置一次性闹铃,执行时间更为精准,为精确闹铃。方法一和二的区别参见上面set的区别。setAlarmClock方法等同于通过setExact方法设置的RTC_WAKEUP类型的闹铃,所以把他归在setExact中介绍。其中AlarmClockInfo实现了Android序列化接口Parcelable,里面包含了mTriggerTime(执行时间)和mShowIntent(执行动作)两个成员变量,可以看做是对闹铃事件的一个封装类。

  • 4.2.3 setInexactRepeating和setRepeating
返回值公开方法
voidsetInexactRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation)
voidsetRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation)

setInexactRepeating和setRepeating两种方法都是用来设置重复闹铃的,setRepeating执行时间更为精准。在Android 4.4之后,Android系统为了省电把时间相近的闹铃打包到一起进行批量处理,这就使得setRepeating方法设置的闹铃不能被精确的执行,必须要使用setExact来代替。

  • 4.2.4 setAndAllowWhileIdle和setExactAndAllowWhileIdle
返回值公开方法
voidsetAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation)
voidsetExactAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation)

使用setAndAllowWhileIdle和setExactAndAllowWhileIdle方法设置一次闹铃,可以在低功耗模式下被执行,setExactAndAllowWhileIdle执行时间更为精准。手机灭屏以后会进入低功耗模式(low-power idle modes),这个时候你会发现通过setExact设置的闹铃也不是100%准确了,需要用setExactAndAllowWhileIdle方法来设置,闹铃才能在低功耗模式下被执行。

  • 4.2.5 setWindow
返回值公开方法
voidsetWindow(int type, long windowStartMillis, long windowLengthMillis, PendingIntent operation)
voidsetWindow(int type, long windowStartMillis, long windowLengthMillis, String tag, AlarmManager.OnAlarmListener listener, Handler targetHandler)

用于设置某个时间段内的一次闹铃。比如,我想在下午的2点到4点之间设置一次提醒。两个方法的区别同set。

 

4.3 取消闹铃

返回值公开方法
voidcancel(PendingIntent operation)
voidcancel(AlarmManager.OnAlarmListener listener)

用于取消设置过的闹铃,分别对应于PendingIntent和AlarmManager.OnAlarmListener方式注册的闹铃。

AlarmManager alarmMgr = (AlarmManager)mContext.getSystemService(Contetxt.ALARM_SERVICE);

alarmMgr.cancel(mPendItForXXX);


//所以说你的PendingIntent ,最好还是存成成员变量。否则,要取消的时候,找不到了。

if ( mPendItForXXX != null)
{
    alarmMgr.cancel(mPendItForXXX);
    mPendItForXXX = null;
}

//这个时候再去设置。这样就是实现了只设置一次。否则,会设置多次时钟,与预期不符。

是不是定义为 private static PendingIntent mPendItForXXX = null; 比较合适。

呵呵,否则,新建一个实例,创建一个PendingIntent, 那累了。

4.4 获得下一次闹铃事件

 

返回值公开方法
AlarmManager.AlarmClockInfogetNextAlarmClock()

用于获得下一次闹铃事件。

 

5 常用时间定义

AlarmManager类已经帮我们定义好了常用的时间常量。

AlarmManager.INTERVAL_FIFTEEN_MINUTES 间隔15分钟
AlarmManager.INTERVAL_HALF_HOUR            间隔半个小时
AlarmManager.INTERVAL_HOUR                       间隔一个小时
AlarmManager.INTERVAL_HALF_DAY               间隔半天
AlarmManager.INTERVAL_DAY                          间隔一天

    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;

6 举个例子操作一下

6.1 初始化PendingIntent

public static final int ALARM_ACTION_CODE= "intent_alarm_code";

Intent intent = new Intent(ALARM_ACTION_CODE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(),
                REQUEST_CODE, intent,
                PendingIntent.FLAG_CANCEL_CURRENT);
  • PendingInent,Intent 更加倾向于去立即执行某个动作,而 PendingIntent 更加倾向于在某个合适的时机去执行某个动作。所以,也可以把 PendingIntent 简单地理解为延迟执行的 Intent。
    getActivity()方法、getBroadcast()方法、getService()方法。分别对应着启动Activity,发送广播,启动service。

  • ALARM_ACTION_CODE,Intent传递给广播接收者的action,自己设定。自己设定用于标记自己的命令的。

  • PendingIntent.FLAG_CANCEL_CURRENT,pendingIntent的第四个参数如果直接传0,表示你不打算通过任何一个flag来控制pendingIntent的创建。下面说说pendingIntent提供的四种flag:

    1. FLAG_CANCEL_CURRENT,如果要创建的PendingIntent已经存在了,那么在创建新的PendingIntent之前,原先已经存在的PendingIntent中的intent将不能使用。
    2. FLAG_NO_CREATE,如果要创建的PendingIntent尚未存在,则不创建新的PendingIntent,直接返回null。
    3. FLAG_ONE_SHOT,相同的PendingIntent只能使用一次,且遇到相同的PendingIntent时不会去更新PendingIntent中封装的Intent的extra部分的内容。
    4. FLAG_UPDATE_CURRENT,如果要创建的PendingIntent已经存在了,那么在保留原先PendingIntent的同时,将原先PendingIntent封装的Intent中的extra部分替换为现在新创建的PendingIntent的intent中extra的内容。

6.2 初始化AlarmManger

AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

设置重复执行的定时任务, 这个就要区分Android版本了

4.4之前,SDK API < 19

alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                SystemClock.elapsedRealtime(), TIME_INTERVAL, pendingIntent);

alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, 
                System.currentTimeMillis(), TIME_INTERVAL, pendingIntent);
  • 第二个参数表示任务首次执行时间:与第一个参数密切相关。第一个参数若为 AlarmManager.ELAPSED_REALTIME_WAKEUP ,那么当前时间就为 SystemClock.elapsedRealtime() ;若为 AlarmManager.RTC_WAKEUP ,那么当前时间就为 System.currentTimeMillis()
  • 第三个参数表示两次执行的间隔时间:这个参数没什么好讲的,一般为常量;
  • 第四个参数表示对应的响应动作:一般都是去发送广播,然后在广播接收 onReceive(Context context, Intent intent) 中做相关操作。

6.0之后的版本,SDK API >= 23

alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,                
                SystemClock.elapsedRealtime(), pendingIntent);

 

        

 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值