通常任务调度机制的实现方式主要有: Thread sleep、Timer、ScheduledExecutor、Handler和其余第三方开源库、android的AlarmManagerjava
一、 Timerandroid
java.util.Timer是Java语言自己提供的一种最简单实现任务调度的方法,使用简单,经过对应的api调用便可。Timer 设计核心是一个TaskQueue和一个TaskThread。api
Timer将接收到的任务丢到本身的 TaskQueue中,TaskQueue按照Task的最初执行时间进行排序。TimerThread 在建立 Timer 时会启动成为一个守护线程,这个守护线程网络
会轮询全部任务,找到一个最近要执行的任务,而后休眠,当到达最近要执行任务的开始时间点,TimerThread 被唤醒并执行该任务。以后 TimerThread 更新最近一个要并发
执行的任务,继续休眠。
ide
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 去执行便可。post
Timer 优势在于简单易用,但因为全部任务都是由同一个线程来调度,所以全部任务都是串行执行的,同一时间只能有一个任务在执行,前一个任务的延迟或异常都将会影响到以后的优化
任务,因此不太适合并发类的任务调度,其实基本上就用不了。ui
为了解决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 取决于每次任务执行的时间长短,是基于不固定时间间隔进行任务调度。
三、 Handler机制
以上两种方式都是由Java语言提供的机制,其实Android 自己也提供了本身的机制。android和AlarmManager都是android提供的。
Handler的postDelayed方法和removeCallbacksAndMessages灵活实现的一种所谓的周期性任务调度,固然也能够借助其余机制来实现开启和中止,一切取决于你的需求。
四、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 }
五、开源工具包 Quartz 与 JCronTab 提供了这方面强大的支持