Android定时任务Timer失效?

Android上定时任务这个需求肯定是经常有的,之前一遇到定时任务,就首选Timer,也没觉得有什么不好的。
后来有一次我测试的时候无意间把系统时间往前调(系统时间调早一点)了一些,发现定时任务的打印停止了,觉得很好奇,以为是进程被干掉了,也就没有在意,又重启了一下应用进程,但是后来再一次出现了这样的事,于是就不淡定了,然后就看了一下Android官方网站上关于Timer的说明,又顺便Google了一下,发现原来是这样的:

在Timer定时器中是以System.currentTimeMillis()来获取当前时间,然后和TimerTask任务下次要执行的时间进行比较,如果时间到了就执行下一次重复任务,比如我用Timer设定的是每隔1s执行一次定时任务,如果你把时间往前调了1个小时,那么Timer定时器在准备执行下次任务的时候获取到的当前时间就比定时器的间隔时间1s大了很多,这样定时器就会等到1个小时零1s才会执行下一次的定时任务,如果你将时间往后调肯定不所以这就是为什么时间往前调Timer定时器会”失效”而往后调不会”失效”的原因

这个是我遇到的Timer定时器的缺陷,网上网友遇到的的Timer的缺陷我总结了一下大致是下面三条(欢迎补充):

时间计算不准

Timer对调度的支持是基于绝对时间,而不是相对时间的,由此,任务对系统时钟的改变是敏感的,更改了系统时间,会导致时间计算不准确,定时任务没有按时完成。

未检查的异常会导致Timer线程终止

如果TimerTask抛出一个未检查的异常,因为Timer不捕获异常,所以会导致整个Timer线程的终止,而且Timer还不会恢复线程的执行,当前的TimerTask和新的任务都不会再执行了

单任务执行

Timer一次只能执行一个任务,后面的任务必须要等前一个任务执行完成之后才可以执行,这样如果前面一个任务耗时超过Timer设定的任务间隔时间,那么后面的一个任务执行时间就会受到影响。
那么既然Timer有这么多缺陷,肯定是还有别的方法来实现定时任务的,那就是Timer的替代者ScheduledThreadPoolExecutor,为什么要说事Timer的替代者呢,我们可以打开Android官网关于Timer类的说明,
可以看到这么一句话:

1
2
Timers schedule one-shot or recurring tasks for execution.
Prefer ScheduledThreadPoolExecutor for new code.

ScheduledThreadPoolExecutor相对于Timer而言优点在于:

  • 可以指定执行定时任务的工作线程(worker threads)数,不再受限于单任务执行
  • ScheduledThreadPoolExecutor使用的是相对时间,是基于时间的延迟,不再受限于调系统时间的问题
  • ScheduledThreadPoolExecutor一个任务发生异常不会影响下一个定时任务的执行
    其实看到这里,以后再遇到定时任务的时候用哪个大家应该都很明白了,因为自己公司的代码不方便贴出,就从网上贴一个简单的ScheduledThreadPoolExecutor的用法:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class ScheduledThreadPoolExecutorTest {
    public static void main(String[] args) {
    ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(2);
    BusinessTask task = new BusinessTask();
    //1秒后开始执行任务,以后每隔2秒执行一次
    executorService.scheduleWithFixedDelay(task, 1000, 2000,TimeUnit.MILLISECONDS);
    }

    private static class BusinessTask implements Runnable{
    @Override
    public void run() {
    try{
    System.out.println("任务开始...");
    doSomething();
    System.out.println("任务结束...");
    }catch (Throwable e) {
    catchException();
    }
    }
    }
    }