今天在改一个开源SpringBoot项目,要拿到Android中使用,所以要去除掉SpringBoot相关的东西,看到项目中使用了SpringBoot的@Scheduled(cron = "0 0 * * * ?")来添加定时器,非常简洁,就这么一个注解就实现了整点执行,每一个小时的整点执行,我要改到Android中去用,Android中不知道怎么使用Spring的注解,所以我就不用Spring,改成普通的Java代码实现。
一个整时执行一次,这样等待的时间太长了,所以我先实现一整秒执行一次,方便测试。使用Timer即可轻松实现,如下:
timer.scheduleAtFixedRate(task, firstTime, period);
如果是整点,假如现在是10:23分,则需要在11:00执行定时器,所以关键点在于拿到下一个小时,如果是整秒,则是拿到下一秒,代码如下:
static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
public static Date getNextHour() {
Calendar calendar = Calendar.getInstance();
System.out.println("当前时间 = " + dateFormat.format(calendar.getTime()));
calendar.set(Calendar.MILLISECOND, 0); // 清空毫秒
calendar.add(Calendar.SECOND, 1); // 添加一秒
Date nextHour = calendar.getTime();
System.out.println("开始时间 = " + dateFormat.format(nextHour));
return nextHour;
}
运行getNextHour()函数,打印结果如下:
当前时间 = 2022-07-11 16:01:50.294
开始时间 = 2022-07-11 16:01:51.000
OK,成功的获取到了下一个整秒,然后就是使用定时器,从下一整秒开始,每隔一秒执行一次:
public static void main(String[] args) {
Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.println(dateFormat.format(new Date()));
}
};
Date firstTime = getNextHour(); // 设置定时器第1次执行的开始时间
long period = TimeUnit.SECONDS.toMillis(1); // 设置每隔1秒执行1次
timer.scheduleAtFixedRate(task, firstTime, period);
}
执行效果如下:
当前时间 = 2022-07-11 16:07:15.570
开始时间 = 2022-07-11 16:07:16.000
2022-07-11 16:07:16.010
2022-07-11 16:07:17.011
2022-07-11 16:07:18.010
2022-07-11 16:07:19.010
2022-07-11 16:07:20.005
2022-07-11 16:07:21.005
可以看到定时器并不是百分百准时的,我们设置的是在16:07:16.000执行,但却是在16:07:16.010执行,有10毫秒的偏差,往后是每隔一秒执行一次,也是有毫秒级的偏差的,像航天航空这种高精度要求的,肯定用不了这种定时器,而对于我们的普通应用,这点影响一般是无关紧要的。
另外,在开始解这个问题的时候,开始时间弄错了,导致发现一个定时器的Bug,比如当前时间已经是15秒了,你却设置为12秒的时候执行,慢了3秒,则一运行代码就会立马执行4次run()函数(为什么不是3次?我也不知道,反正慢4秒就执行5次,慢5秒就执行6次,就这个规律),示例如下:
把设置秒的地方设置为负3秒,如下:
calendar.add(Calendar.SECOND, -3);
运行效果如下:
当前时间 = 2022-07-11 16:14:26.325
开始时间 = 2022-07-11 16:14:23.000
2022-07-11 16:14:26.340
2022-07-11 16:14:26.340
2022-07-11 16:14:26.340
2022-07-11 16:14:26.340
2022-07-11 16:14:27.012
2022-07-11 16:14:28.012
所以,定时器的开始时间不能弄错,不能设置的比当前时间早,设置的早了就会出现这样的问题。
实现了整秒的定时器,再实现整点的定时器就很简单了,代码如下:
static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
public static void main(String[] args) {
Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.println(dateFormat.format(new Date()));
}
};
Date firstTime = getNextHour(); // 设置定时器第1次执行的开始时间
long period = TimeUnit.HOURS.toMillis(1); // 设置每隔1小时执行1次
timer.scheduleAtFixedRate(task, firstTime, period);
}
/** 获取当前时间的下一个小时的整点时间 */
public static Date getNextHour() {
Calendar calendar = Calendar.getInstance();
System.out.println("当前时间 = " + dateFormat.format(calendar.getTime()));
calendar.set(Calendar.MINUTE, 0); // 清空分
calendar.set(Calendar.SECOND, 0); // 清空秒
calendar.set(Calendar.MILLISECOND, 0); // 清空毫秒
calendar.add(Calendar.HOUR, 1); // 添加1小时
Date nextHour = calendar.getTime();
System.out.println("开始时间 = " + dateFormat.format(nextHour));
return nextHour;
}
执行效果如下:
当前时间 = 2022-07-11 16:24:56.510
开始时间 = 2022-07-11 17:00:00.000
2022-07-11 17:00:00.013
2022-07-11 18:00:00.006
2022-07-11 19:00:00.009
2022-07-11 20:00:00.011
2022-07-11 21:00:00.001
2022-07-11 22:00:00.005
2022-07-11 23:00:00.009
2022-07-12 00:00:00.014
2022-07-12 01:00:00.018
2022-07-12 02:00:00.004
2022-07-12 03:00:01.233
2022-07-12 04:00:00.000
2022-07-12 05:00:00.010
2022-07-12 06:00:00.002
2022-07-12 07:00:00.005
2022-07-12 08:00:00.004
从结果可以看到,没有24点,只有23点,23:59之后就是00:00,这是新的一天了,所以00:00是新的一天的开始,如果用24:00会让人以为还没到新的一天!在凌晨3点的时候误差有1秒多,后面又自动究正了!