今天在改一个开源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秒多,后面又自动究正了!