• android周期性任务


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

  • 相关阅读:
    关于LEA指令(单周期就可以做简单的算术计算)
    说说字符集和编码(非常形象,非常有意思)
    类依赖项的不透明性和透明性
    构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(6)-Unity 2.x依赖注入by运行时注入[附源码]
    大端和小端
    Google C++测试框架系列:入门
    随机森林中的数学基础之大数定律
    K-means Algorithm
    JavaScript插件——按钮
    分布式系统设计(8)
  • 原文地址:https://www.cnblogs.com/linghu-java/p/10002879.html
Copyright © 2020-2023  润新知