android周期性任务

一般任务调度机制的实现方式主要有: Thread sleep、Timer、ScheduledExecutor、Handler和其他第三方开源库、android的AlarmManager

1、 Timer

  java.util.Timer是Java语言本身提供的一种最简单实现任务调度的方法,使用简单,通过对应的api调用即可。Timer 设计核心是一个TaskQueue和一个TaskThread。

Timer将接收到的任务丢到自己的 TaskQueue中,TaskQueue按照Task的最初执行时间进行排序。TimerThread 在创建 Timer 时会启动成为一个守护线程,这个守护线程

会轮询所有任务,找到一个最近要执行的任务,然后休眠,当到达最近要执行任务的开始时间点,TimerThread 被唤醒并执行该任务。之后 TimerThread 更新最近一个要

执行的任务,继续休眠。
  

 1 public class PeriodOperation{
 2 
 3     public static void main(String[] args) {
 4         testTimer();
 5     }
 6 
 7     private static void testTimer(){
 8         Timer timer = new Timer(); 
9 long delay1 = 1 * 1000; 10 long period1 = 1000; 11 // 从现在开始 1 秒钟之后,每隔 1 秒钟执行一次 job1 12 timer.schedule(new TimerTask1("job1"), delay1, period1);

13     long delay2 = 2 * 1000; 14 long period2 = 2000; 15 // 从现在开始 2 秒钟之后,每隔 2 秒钟执行一次 job2 16 timer.schedule(new TimerTask1("job2"), delay2, period2); 17 } 18 19 static class TimerTask1 extends TimerTask{ 20 private String jobName = ""; 21 public TimerTask1(String job) { 22 super(); 23 this.jobName = job; 24 } 25 @Override 26 public void run() { 27 System.out.println("执行作业:" + jobName); 28 } 29 } 30 }

  Timer机制实现任务调度的核心类是 Timer 和 TimerTask。其中 Timer 负责设定 TimerTask 的起始与间隔执行时间。使用者只需要创建一个 TimerTask 的继承类,实现自己的 run 方法,

然后将其丢给 Timer 去执行即可。

  Timer 优点在于简单易用,但由于所有任务都是由同一个线程来调度,因此所有任务都是串行执行的,同一时间只能有一个任务在执行,前一个任务的延迟或异常都将会影响到之后的

任务,所以不太适合并发类的任务调度,其实基本上就用不了。

2、ScheduledExecutor

  为了解决Timer 设计上的缺陷,在Java 5推出了基于线程池设计的 ScheduledExecutor。其设计思想是:每一个被调度的任务都会由线程池中一个线程去执行,因此任务是并发执行的,

相互之间不会受到干扰。但需要注意的是只有当任务的执行时间到来时,ScheduedExecutor 才会真正启动一个线程,其余时间ScheduledExecutor 都是在轮询任务的状态。

 1 import java.text.SimpleDateFormat;
 2 import java.util.Date;
 3 import java.util.Timer;
 4 import java.util.TimerTask;
 5 import java.util.concurrent.Executors;
 6 import java.util.concurrent.ScheduledExecutorService;
 7 import java.util.concurrent.TimeUnit;
 8 
 9 public class PeriodOperation {
10 
11     public static void main(String[] args) {
12          testScheduledExecutor();
13     }
14 
15     private static void testScheduledExecutor(){
16         ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
17 
18         long initialDelay1 = 1;
19         long period1 = 1;
20         // 从现在开始1秒钟之后,每隔1秒钟执行一次job1
21         service.scheduleAtFixedRate(new Task1("job1"), initialDelay1,  period1, TimeUnit.SECONDS);
22 
23         long initialDelay2 = 1;
24         long delay2 = 1;
25         // 从现在开始2秒钟之后,每隔2秒钟执行一次job2
26         service.scheduleWithFixedDelay(new Task1("job2"), initialDelay2, delay2, TimeUnit.SECONDS);
27     }
28 
29     static class Task1 implements Runnable{
30         private String jobName = "";
31 
32         public Task1 (String jobName) {
33             super();
34             this.jobName = jobName;
35         }
36 
37         @Override
38         public void run() {
39             System.out.println(getNowTime()+"执行作业:" + jobName);
40         }
41     }
42 
43     public static String getNowTime(){
44         Date now = new Date();
45         SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
46         return dateFormat.format(now);
47     }
48 
49 }

ScheduledExecutorService 中两种最常用的调度方法 ScheduleAtFixedRate 和 ScheduleWithFixedDelay。

其中ScheduleAtFixedRate 每次执行时间为上一次任务开始起向后推一个时间间隔,即每次执行时间为:  initialDelay,  initialDelay+period,  initialDelay+2*period, …;

而ScheduleWithFixedDelay 每次执行时间为上一次任务结束起向后推一个时间间隔,即每次执行时间为:initialDelay, initialDelay+executeTime+delay, initialDelay+2*executeTime+2*delay。

所以两种方式异同在于ScheduleAtFixedRate 是基于固定时间间隔进行任务调度,ScheduleWithFixedDelay 取决于每次任务执行的时间长短,是基于不固定时间间隔进行任务调度。

3、 Handler机制

  以上两种方式都是由Java语言提供的机制,其实Android 本身也提供了自己的机制。android和AlarmManager都是android提供的。

Handler的postDelayed方法和removeCallbacksAndMessages灵活实现的一种所谓的周期性任务调度,当然也可以借助其他机制来实现开启和停止,一切取决于你的需求。

4、AlarmManager

  Alarm超出了应用程序的作用域,所以它们可以用于触发应用程序事件或动作,甚至在应用程序关闭之后。与Broadcast Receiver结合,它们可以变得尤其的强大,可以通过设置Alarm来启动应用程序或者执行动作,而应用程序不需要打开或者处于活跃状态。举个例子,你可以使用Alarm来实现一个闹钟程序,执行正常的网络查询,或者在“非高峰”时间安排耗时或有代价的操作。 

对于仅在应用程序生命周期内发生的定时操作,Handler类与Timer和Thread类的结合是一个更好的选择,它允许Android更好地控制系统资源。 

Android中的Alarm在设备处于睡眠模式时仍保持活跃,它可以设置来唤醒设备。然而,所有的Alarm在设备重启时都会被取消。

AlarmManager设置后不准确的问题:如下爆出的问题

  

11-22 06:05:31.251 28276-28276/com.xxx.fresh D/Linghu DuringService.onStartCommand: ==============AM during=2018-11-22 06:45:05,1970-01-01 08:39:29
11-22 06:05:31.272 28276-28276/com.xxx.fresh D/Linghu DuringService.onStartCommand: ==============AM during=2018-11-22 06:45:05,1970-01-01 08:39:29
11-22 06:05:31.784 28276-28276/com.xxx.fresh D/Linghu DuringService.onStartCommand: ==============AM during=2018-11-22 06:45:05,1970-01-01 08:39:29
11-22 06:05:31.797 28276-28276/com.xxx.fresh D/Linghu DuringService.onStartCommand: ==============AM during=2018-11-22 06:45:05,1970-01-01 08:39:29
11-22 06:45:05.822 28276-28276/com.xxx.fresh D/Linghu DuringService.onStartCommand: ==============AM during=2018-11-22 07:00:05,1970-01-01 08:14:55
11-22 06:45:06.108 28276-28276/com.xxx.fresh D/Linghu DuringService.onStartCommand: ==============AM during=2018-11-22 07:00:05,1970-01-01 08:14:54
11-22 07:05:31.276 28276-28276/com.xxx.fresh D/Linghu DuringService.onStartCommand: ==============AM during=2018-11-22 07:45:05,1970-01-01 08:39:29
11-22 07:05:31.605 28276-28276/com.xxx.fresh D/Linghu DuringService.onStartCommand: ==============AM during=2018-11-22 07:45:05,1970-01-01 08:39:29
11-22 07:45:05.637 28276-28276/com.xxx.fresh D/Linghu DuringService.onStartCommand: ==============AM during=2018-11-22 08:00:05,1970-01-01 08:14:55
11-22 07:45:06.092 28276-28276/com.xxx.fresh D/Linghu DuringService.onStartCommand: ==============AM during=2018-11-22 08:00:05,1970-01-01 08:14:54
11-22 08:05:31.269 28276-28276/com.xxx.fresh D/Linghu DuringService.onStartCommand: ==============AM during=2018-11-22 08:45:05,1970-01-01 08:39:29
11-22 08:05:31.632 28276-28276/com.xxx.fresh D/Linghu DuringService.onStartCommand: ==============AM during=2018-11-22 08:45:05,1970-01-01 08:39:28
11-22 08:45:05.022 28276-28276/com.xxx.fresh D/Linghu DuringService.onStartCommand: ==============AM during=2018-11-22 09:00:05,1970-01-01 08:14:54
11-22 08:45:05.482 28276-28276/com.xxx.fresh D/Linghu DuringService.onStartCommand: ==============AM during=2018-11-22 09:00:05,1970-01-01 08:14:54
11-22 09:05:31.261 28276-28276/com.xxx.fresh D/Linghu DuringService.onStartCommand: ==============AM during=2018-11-22 09:45:05,1970-01-01 08:39:28
11-22 09:05:31.614 28276-28276/com.xxx.fresh D/Linghu DuringService.onStartCommand: ==============AM during=2018-11-22 09:45:05,1970-01-01 08:39:28
11-22 09:45:05.019 28276-28276/com.xxx.fresh D/Linghu DuringService.onStartCommand: ==============AM during=2018-11-22 10:00:05,1970-01-01 08:14:54
11-22 09:45:05.474 28276-28276/com.xxx.fresh D/Linghu DuringService.onStartCommand: ==============AM during=2018-11-22 10:00:05,1970-01-01 08:14:54
11-22 10:05:31.255 28276-28276/com.xxx.fresh D/Linghu DuringService.onStartCommand: ==============AM during=2018-11-22 10:45:05,1970-01-01 08:39:28
11-22 10:05:31.610 28276-28276/com.xxx.fresh D/Linghu DuringService.onStartCommand: ==============AM during=2018-11-22 10:45:05,1970-01-01 08:39:28
11-22 10:45:05.031 28276-28276/com.xxx.fresh D/Linghu DuringService.onStartCommand: ==============AM during=2018-11-22 11:00:05,1970-01-01 08:14:54
11-22 10:45:10.845 28276-28276/com.xxx.fresh D/Linghu DuringService.onStartCommand: ==============AM during=2018-11-22 11:00:05,1970-01-01 08:14:49
11-22 10:55:57.534 28276-28276/com.xxx.fresh D/Linghu DuringService.onStartCommand: ==============AM during=2018-11-22 11:00:05,1970-01-01 08:04:02

11-22 06:05:31.260 28146-28146/com.xxx.card D/Linghu DuringReceiver.onReceive: =========DuringReceiver course2018-11-22 06:05:31
11-22 06:45:05.817 28146-28146/com.xxx.card D/Linghu DuringReceiver.onReceive: =========DuringReceiver course2018-11-22 06:45:05
11-22 07:05:31.251 28146-28146/com.xxx.card D/Linghu DuringReceiver.onReceive: =========DuringReceiver course2018-11-22 07:05:31
11-22 07:45:05.629 28146-28146/com.xxx.card D/Linghu DuringReceiver.onReceive: =========DuringReceiver course2018-11-22 07:45:05
11-22 08:05:31.251 28146-28146/com.xxx.card D/Linghu DuringReceiver.onReceive: =========DuringReceiver course2018-11-22 08:05:31
11-22 08:45:05.013 28146-28146/com.xxx.card D/Linghu DuringReceiver.onReceive: =========DuringReceiver course2018-11-22 08:45:05
11-22 09:05:31.249 28146-28146/com.xxx.card D/Linghu DuringReceiver.onReceive: =========DuringReceiver course2018-11-22 09:05:31
11-22 09:45:05.012 28146-28146/com.xxx.card D/Linghu DuringReceiver.onReceive: =========DuringReceiver course2018-11-22 09:45:05
11-22 10:05:31.246 28146-28146/com.xxx.card D/Linghu DuringReceiver.onReceive: =========DuringReceiver course2018-11-22 10:05:31
11-22 10:45:05.017 28146-28146/com.xxx.card D/Linghu DuringReceiver.onReceive: =========DuringReceiver course2018-11-22 10:45:05
11-22 11:00:05.017 28146-28146/com.xxx.card D/Linghu DuringReceiver.onReceive: =========DuringReceiver course2018-11-22 11:00:05

是因为manager.set(AlarmManager.RTC_WAKEUP, triggerAtTime, pIntent);在android的高版本上回因为android系统的优化、睡眠原因变得不灵敏,高版本需要使用setExact()就可以。

 1     AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
 2         Intent repeatIntent = new Intent(this, DuringReceiver.class);
 3         repeatIntent.setAction(ACTION_DURING_RECEIVER_COURSE);
 4         PendingIntent pIntent = PendingIntent.getBroadcast(this, 1000, repeatIntent, 0);
 5         long triggerAtTime = System.currentTimeMillis() + nextDiffTime + 2000;
 6         L.d("==============Alarm:" + FormatUtilsKt.getDataAllFormatTime(triggerAtTime) +",nextDiff:"+ FormatUtilsKt.getHhMmTime(nextDiffTime));
 7 
 8         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //4.4以上 API19
 9             manager.setExact(AlarmManager.RTC_WAKEUP, triggerAtTime, pIntent);
10         }else{
11             manager.set(AlarmManager.RTC_WAKEUP, triggerAtTime, pIntent);
12         }

 

5、开源工具包 Quartz 与 JCronTab 提供了这方面强大的支持

posted @ 2018-11-22 18:27  linghu_java  阅读(2207)  评论(0编辑  收藏  举报