Android 创建定时任务

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/dashujua/article/details/81025773

        Android 中的定时任务一般有两种实现方式,一种是使用Java API 里提供的 Timer 类,一种是使用 Android 的Alarm 机制。两种方式在大部分情况下都能实现类似的效果,但是 Timer 类有一个缺点,它并不适合用于那些需要长期在后台执行的定时任务。一般情况下,为了能让手机电池更加耐用,每种手机都有自己的休眠策略,Android 手机会在长时间不操作的情况下自动进入休眠状态,这就有可能导致 Timer 中的定时任务无法正确运行。而 Alarm 则具有唤醒 CPU 的功能 ,它可以保证在大多数情况下需要执行定时任务的时候CPU都能正常工作。(唤醒CPU和唤醒屏幕并不是一个概念)


    Java Timer 类
    Timer 是一个工具类,通过调度 TimerTasks 安排在后台执行的任务(单次任务/周期任务); TimerTask 是一个抽象类,继承它并实现其 run 方法来写具体的任务逻辑代码。

    根据是否循环执行分为两类:

        1.只执行一次      
    public void schedule(TimerTask task, long delay) //从现在起过 delay 毫秒后开始执行任务
    public void schedule(TimerTask task, Date time) //在指定时间 time 到来后开始执行任务
 1         Timer timer = new Timer();
 2 
 3         //延迟1000ms执行程序
 4         timer.schedule(new TimerTask() {
 5             @Override
 6             public void run() {
 7                 System.out.println("当前时间..." + this.scheduledExecutionTime());
 8             }
 9         }, 1000);
10         //延迟10000ms执行程序
11         timer.schedule(new TimerTask() {
12             @Override
13             public void run() {
14                 System.out.println("当前时间..." + this.scheduledExecutionTime());
15             }
16         }, new Date(System.currentTimeMillis() + 10000));
       
         2.循环执行    

    public void schedule(TimerTask task, long delay, long period) //从现在起过 delay 毫秒后,每隔 period 毫秒执行一次任务
    public void schedule(TimerTask task, Date firstTime, long period) //在指定时间 firstTime 到来后,每隔 period 毫秒执行一次任务
 1         Timer timer = new Timer();
 2         
 3         //前一次执行程序结束后 2000ms 后开始执行下一次程序
 4         timer.schedule(new TimerTask() {
 5             @Override
 6             public void run() {
 7                 System.out.println("当前时间..." + this.scheduledExecutionTime());
 8             }
 9         }, 0,2000);
10 
11         //前一次程序执行开始 后 2000ms后开始执行下一次程序
12         timer.scheduleAtFixedRate(new TimerTask() {
13             @Override
14             public void run() {
15                 System.out.println("当前时间..." + this.scheduledExecutionTime());
16             }
17         },0,2000);

    在初始化 Timer 时,开启一个线程循环提取任务数组中的任务,若任务数据为空,线程等待知道添加任务;

    当添加任务时,唤醒线程,提取数组中标记为1的任务,

    若该任务状态为CANCELLED,则从数组中删除任务, continue ,继续循环提取任务;

    然后将系统当前时间和任务执行时间点进行比较 标记 taskFired=(executionTime <= currentTime)

       taskFired = false,说明任务执行时间点还没到,则调用wait等待时间长度(executionTime <= currentTime),然后重新提取该任务;

        taskFired = true,说明任务执行时间点已到,或过去了。继续判断任务循环时间间隔 period:

                     若 period = 0,说明此次任务是非循环任务,直接将该任务从任务数组中删除,并将其状态置为EXECUTED,接着执行其run方法;

                    若period != 0,说明此次任务是周期任务,则将该任务的执行时间点往前推进,具体推进时间根据调用方法判断:

                                                如果是schedule方法,则在当前时间基础上向前推进period时间长度;

                                                如果是scheduleAtFixedRate方法,则在当前任务执行时间点基础上向前推进period时间长度,

                                                最后执行run方法;

                                                循环提取任务

    Alarm 机制
    其主要通过 AlarmManager 类来实现:
    
        AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
        //定时一小时
        int anHour = 60 * 60 * 1000;
        long triggerAtTime = SystemClock.elapsedRealtime() + anHour;
        Intent updateIntent = new Intent(this,MyService.class);
        PendingIntent pi = PendingIntent.getService(this,0,updateIntent,0);
        manager.cancel(pi);
        manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime,pi);
    先获取到AlarmManager 实例,接着定义任务的触发时间,再使用PendingIntent 指定处理定时任务的服务类为MyService,最后调用set方法来完成任务设定。

    最后在想要启动服务的地方添加以下代码:
        Intent intent = new Intent(this, AutoUpdateService.class);
        startService(intent);

    set()方法共有三个参数

    --第一个参数用于指定AlarmManager 的工作类型,有四个值可以选择

            --ELAPSED_REALTIME

            --ELAPSED_REALTIME_WAKEUP

            --RTC

            --RTC_WAKEUP 

    前两个表示定时任务的触发时间从系统开机后开始计算,不带WAKEUP的不会唤醒CPU,带WAKEUP的会唤醒CPU。

    后两个表示定时任务的触发时间从1970年1月1日0点开始计算,不带WAKEUP的不会唤醒CPU,带WAKEUP的会唤醒CPU。

    其中计算毫秒数有两个方法,SystemClock.elapsedRealTime()方法可以获取到系统开机至今所经历的的毫秒数,System.currentTimeMillis()方法可以获取到1970年1月1日0点至今所经历的的毫秒数。

 --第二个参数指定任务触发的时间,以毫秒为单位。若第一个参数使用的是ELAPSED_REALTIME或ELAPSED_REALTIME_WAKEUP时,传入系统开机至今的时间再加上任务延迟执行的时间。若第一个参数使用的是RTC或RTC_WAKEUP,则传入1970年1月1日0点至今的时间再加上任务延迟执行的时间。

  --第三个参数传入一个PendingIndent,一般会调用 getSevice()或getBroadCast()方法来获取一个能够执行服务或广播的PendingIntent。当任务被触发时,服务的onStartCommand()方法或广播接收器的onReceive()方法就可以得到执行。

 
 

    

猜你喜欢

转载自blog.csdn.net/dashujua/article/details/81025773