一般任务调度机制的实现方式主要有: 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 提供了这方面强大的支持

android周期性任务的更多相关文章

  1. Android Camera开发:周期性循环自动聚焦auto focus挂掉原因分析(preview is not enabled)

    参考:Android Camera开发:扫描二维码,周期性循环自动聚焦auto focus挂掉原因分析(preview is not enabled) 最近做Android人脸识别时,camera在自 ...

  2. Android开发趣事记之周期性广告

    前些天做了一个应用,由于怕影响用户体验,所以我将广告设定了一下,就是每启动软件8次.就会弹出一次广告. 在上传到应用宝后.竟然得到了这种结果: 看到了吧.无病毒,无广告. 看来审核人员是不会把应用连续 ...

  3. 虾扯蛋:Android View动画 Animation不完全解析

    本文结合一些周知的概念和源码片段,对View动画的工作原理进行挖掘和分析.以下不是对源码一丝不苟的分析过程,只是以搞清楚Animation的执行过程.如何被周期性调用为目标粗略分析下相关方法的执行细节 ...

  4. android自定义控件一站式入门

    自定义控件 Android系统提供了一系列UI相关的类来帮助我们构造app的界面,以及完成交互的处理. 一般的,所有可以在窗口中被展示的UI对象类型,最终都是继承自View的类,这包括展示最终内容的非 ...

  5. Android Weekly Notes Issue #234

    Android Weekly Issue #234 December 4th, 2016 Android Weekly Issue #234 本期内容包括: ConstraintLayout的使用; ...

  6. Android线程管理之ExecutorService线程池

    前言: 上篇学习了线程Thread的使用,今天来学习一下线程池ExecutorService. 线程管理相关文章地址: Android线程管理之Thread使用总结 Android线程管理之Execu ...

  7. (转) Android开发性能优化简介

    作者:贺小令 随着技术的发展,智能手机硬件配置越来越高,可是它和现在的PC相比,其运算能力,续航能力,存储空间等都还是受到很大的限制,同时用户对手机的体验要求远远高于PC的桌面应用程序.以上理由,足以 ...

  8. Android的系统服务一览

    System_Server进程 运行在system server进程中的服务比较多,这是整个Android框架的基础 Native服务 SurfaceFlinger 这是framebuffer合成的服 ...

  9. Android开发5:应用程序窗口小部件App Widgets的实现

    前言 本次主要是实现一个Android应用,实现静态广播.动态广播两种改变 widget内容的方法,即在上篇博文中实验的基础上进行修改,所以此次实验的重点是AppWidget小部件的实现啦~ 首先,我 ...

随机推荐

  1. libUpnp缓冲区溢出、拒绝服务等漏洞分析

    该漏洞存在于UPnP™设备的便携式SDK中,也叫做 libupnp.这个库是用来实现媒体播放(DLAN)或者NAT地址转换(UPnP IGD).智能手机上的应用程序可用这些功能播放媒体文件或者利用用户 ...

  2. Html解析类的新选择CsQuery

    今天在做一个html解析的方法,以前用HtmlAgilityPack或Winista.HTMLParser. 现在发现了一个巨好用的项目叫CsQuery,这货据说不仅能解析html还能提取css. 选 ...

  3. Python内置类型性能分析

    Python内置类型性能分析 timeit模块 timeit模块可以用来测试一小段Python代码的执行速度. class timeit.Timer(stmt='pass', setup='pass' ...

  4. Spring @Configuration

    下面是一个典型的spring配置文件(application-config.xml): <beans> <bean id="orderService" class ...

  5. date.getTime()

    Date date = new Date(); System.out.println(date.getTime()); 输出结果是1210745780625 编译时间当时时间大概是2008年5.14好 ...

  6. 格式化java8 LocalDateTime

    DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.CHINA).format(time1);

  7. PAT L3-008 喊山(广搜)

    喊山,是人双手围在嘴边成喇叭状,对着远方高山发出“喂—喂喂—喂喂喂……”的呼唤.呼唤声通过空气的传递,回荡于深谷之间,传送到人们耳中,发出约定俗成的“讯号”,达到声讯传递交流的目的.原来它是彝族先民用 ...

  8. c++泛型模板

    模板是C++支持参数化多态的工具,使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数.返回值取得任意类型. 模板是一种对类型进行参数化的工具: 通常有两种形式:函 ...

  9. 【校招面试 之 剑指offer】第9-2题 用两个队列实现一个栈

    #include<iostream> #include<queue> using namespace std; // 对于出栈解决的思路是:将queue1的元素除了最后一个外全 ...

  10. html标签的总结-重复

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...