一、长时间定时任务,比较Timer、Hnadler、AlarmManager
Timer,有一个明显的问题,它并不太适合用于需要长期在后台运行的定时任务。我们都知道,为了能让电池更加耐用,每种手机都会有自己的休眠策略,Android手机就会在长时间不操作的情况下自动让CPU进入睡眠状态,这就有可能导致Timer中的定时任务无法正常运行。
Hnadler,Handler的postDelay方法也可以实现定时操作,它同样也是不靠谱的,因为默认Hnadler依赖于线程(main线程或者子线程),所以只要进程被杀死,所有相关的线程都被晒死,所以handler中的定时操作就无效了。Timer也一样,因为Timer实际上是另起一个子线程,进程被杀,子线程当然也被杀了。
AlarmManager,它通过pendingIntent具有唤醒未启动进程的功能,即可以保证每次需要执行定时任务的时候CPU都能正常工作。但是当设备关机和重启后,闹钟将被清除。
二、使用AlarmManager
通过上面的分析,我们可以明显感受到AlarmManager在长时间定时任务中的优点,下面来说说它的用法(由于AlarmManager在不同的Android版本都有修改,所以如果想使用AlarmManager需要处理不同版本问题)
1. 初始化PendingIntent
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()方法ALARM_ACTION_CODE,Intent传递给广播接收者的action,自己设定。
-
PendingIntent.FLAG_CANCEL_CURRENT,pendingIntent的第四个参数如果直接传0,表示你不打算通过任何一个flag来控制pendingIntent的创建。下面说说pendingIntent提供的四种flag:
- FLAG_CANCEL_CURRENT,如果要创建的PendingIntent已经存在了,那么在创建新的PendingIntent之前,原先已经存在的PendingIntent中的intent将不能使用。
- FLAG_NO_CREATE,如果要创建的PendingIntent尚未存在,则不创建新的PendingIntent,直接返回null。
- FLAG_ONE_SHOT,相同的PendingIntent只能使用一次,且遇到相同的PendingIntent时不会去更新PendingIntent中封装的Intent的extra部分的内容。
- FLAG_UPDATE_CURRENT,如果要创建的PendingIntent已经存在了,那么在保留原先PendingIntent的同时,将原先PendingIntent封装的Intent中的extra部分替换为现在新创建的PendingIntent的intent中extra的内容。
那么如何比较新老pendingIntent是否相同呢?
PendingIntent重写了equals方法,判定两个PendingIntent是否相同的依据是它封装的Intent是否“相同”和requestCode是否一致。注意,“相同”上打了引号,这是因为在比较PendingIntent中封装的intent时是否相同时,使用的是Intent的filterEquals方法,该方法认为只要两个intent具有相同的action、data、categories、components、type和flags(这个flags是intent的flags)就认为它们两个是“相同”的,filterEquals是不会比较两个intent的extra部分和内存地址的。
另外需要注意的是,Intent并没有重写equals方法,所以如果使用Intent的equals方法比较两个intent对象的话,比较的是两个对象的内存地址。
2. 初始化AlarmManager
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
3. 设置重复执行的定时任务
这个就要区分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
或者AlarmManager.RTC_WAKEUP
。它们之间的区别就是前者是从手机开机后的时间,包含了手机睡眠时间;而后者使用的就是手机系统设置中的时间。所以如果设置为AlarmManager.RTC_WAKEUP
,那么可以通过修改手机系统的时间来提前触发定时事件。另外,对于相似的AlarmManager.ELAPSED_REALTIME
和AlarmManager.RTC
来说,它们不会唤醒 CPU 。所以使用的频率较少; - 第二个参数表示任务首次执行时间:与第一个参数密切相关。第一个参数若为
AlarmManager.ELAPSED_REALTIME_WAKEUP
,那么当前时间就为SystemClock.elapsedRealtime()
;若为AlarmManager.RTC_WAKEUP
,那么当前时间就为System.currentTimeMillis()
; - 第三个参数表示两次执行的间隔时间:这个参数没什么好讲的,一般为常量;
- 第四个参数表示对应的响应动作:一般都是去发送广播,然后在广播接收
onReceive(Context context, Intent intent)
中做相关操作。
4.4之后,6.0之前, SDK API >= 19, SDK API < 23
alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(), pendingIntent);
查阅 Android 官网中关于 Android 4.4 API 会看到如下几句话:
6.0之后的版本,SDK API >= 23
alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(), pendingIntent);
再次打开 Android 官网中关于 Android 6.0 变更 ,发现在 Android 6.0 中引入了低电耗模式和应用待机模式。然后接着往下看 对低电耗模式和应用待机模式进行针对性优化 ,发现会有下面一段话: