Android 中的定时任务一般有两种实现方式,一种是使用Java API 里提供的 Timer 类,一种是使用 Android 的Alarm 机制。两种方式在大部分情况下都能实现类似的效果,但是 Timer 类有一个缺点,它并不适合用于那些需要长期在后台执行的定时任务。一般情况下,为了能让手机电池更加耐用,每种手机都有自己的休眠策略,Android 手机会在长时间不操作的情况下自动进入休眠状态,这就有可能导致 Timer 中的定时任务无法正确运行。而 Alarm 则具有唤醒 CPU 的功能 ,它可以保证在大多数情况下需要执行定时任务的时候CPU都能正常工作。(唤醒CPU和唤醒屏幕并不是一个概念)
Java Timer 类
根据是否循环执行分为两类:
1.只执行一次
public void schedule(TimerTask task, long delay) //从现在起过 delay 毫秒后开始执行任务
public void schedule(TimerTask task, Date time) //在指定时间 time 到来后开始执行任务
1 Timer timer = new Timer();
2
3 //延迟1000ms执行程序
4 timer.schedule(new TimerTask() {
5 @Override
6 public void run() {
7 System.out.println("当前时间..." + this.scheduledExecutionTime());
8 }
9 }, 1000);
10 //延迟10000ms执行程序
11 timer.schedule(new TimerTask() {
12 @Override
13 public void run() {
14 System.out.println("当前时间..." + this.scheduledExecutionTime());
15 }
16 }, new Date(System.currentTimeMillis() + 10000));
2.循环执行
public void schedule(TimerTask task, long delay, long period) //从现在起过 delay 毫秒后,每隔 period 毫秒执行一次任务
public void schedule(TimerTask task, Date firstTime, long period) //在指定时间 firstTime 到来后,每隔 period 毫秒执行一次任务
1 Timer timer = new Timer();
2
3 //前一次执行程序结束后 2000ms 后开始执行下一次程序
4 timer.schedule(new TimerTask() {
5 @Override
6 public void run() {
7 System.out.println("当前时间..." + this.scheduledExecutionTime());
8 }
9 }, 0,2000);
10
11 //前一次程序执行开始 后 2000ms后开始执行下一次程序
12 timer.scheduleAtFixedRate(new TimerTask() {
13 @Override
14 public void run() {
15 System.out.println("当前时间..." + this.scheduledExecutionTime());
16 }
17 },0,2000);
在初始化 Timer 时,开启一个线程循环提取任务数组中的任务,若任务数据为空,线程等待知道添加任务;
当添加任务时,唤醒线程,提取数组中标记为1的任务,
若该任务状态为CANCELLED,则从数组中删除任务, continue ,继续循环提取任务;
然后将系统当前时间和任务执行时间点进行比较 标记 taskFired=(executionTime <= currentTime)
taskFired = false,说明任务执行时间点还没到,则调用wait等待时间长度(executionTime <= currentTime),然后重新提取该任务;
taskFired = true,说明任务执行时间点已到,或过去了。继续判断任务循环时间间隔 period:
若 period = 0,说明此次任务是非循环任务,直接将该任务从任务数组中删除,并将其状态置为EXECUTED,接着执行其run方法;
若period != 0,说明此次任务是周期任务,则将该任务的执行时间点往前推进,具体推进时间根据调用方法判断:
如果是schedule方法,则在当前时间基础上向前推进period时间长度;
如果是scheduleAtFixedRate方法,则在当前任务执行时间点基础上向前推进period时间长度,
最后执行run方法;
循环提取任务
Alarm 机制
AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
//定时一小时
int anHour = 60 * 60 * 1000;
long triggerAtTime = SystemClock.elapsedRealtime() + anHour;
Intent updateIntent = new Intent(this,MyService.class);
PendingIntent pi = PendingIntent.getService(this,0,updateIntent,0);
manager.cancel(pi);
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime,pi);
先获取到AlarmManager 实例,接着定义任务的触发时间,再使用PendingIntent 指定处理定时任务的服务类为MyService,最后调用set方法来完成任务设定。
最后在想要启动服务的地方添加以下代码:
Intent intent = new Intent(this, AutoUpdateService.class);
startService(intent);
set()方法共有三个参数
--第一个参数用于指定AlarmManager 的工作类型,有四个值可以选择
--ELAPSED_REALTIME
--ELAPSED_REALTIME_WAKEUP
--RTC
--RTC_WAKEUP
前两个表示定时任务的触发时间从系统开机后开始计算,不带WAKEUP的不会唤醒CPU,带WAKEUP的会唤醒CPU。
后两个表示定时任务的触发时间从1970年1月1日0点开始计算,不带WAKEUP的不会唤醒CPU,带WAKEUP的会唤醒CPU。
其中计算毫秒数有两个方法,SystemClock.elapsedRealTime()方法可以获取到系统开机至今所经历的的毫秒数,System.currentTimeMillis()方法可以获取到1970年1月1日0点至今所经历的的毫秒数。
--第二个参数指定任务触发的时间,以毫秒为单位。若第一个参数使用的是ELAPSED_REALTIME或ELAPSED_REALTIME_WAKEUP时,传入系统开机至今的时间再加上任务延迟执行的时间。若第一个参数使用的是RTC或RTC_WAKEUP,则传入1970年1月1日0点至今的时间再加上任务延迟执行的时间。
--第三个参数传入一个PendingIndent,一般会调用 getSevice()或getBroadCast()方法来获取一个能够执行服务或广播的PendingIntent。当任务被触发时,服务的onStartCommand()方法或广播接收器的onReceive()方法就可以得到执行。