android的定时任务

时间:2021-10-09 08:01:23

android的定时任务有两种实现方式:Timer和Alarm机制。


在Android开发中,定时执行任务的3种实现方法:

一、采用Handler与线程的sleep(long)方法(不建议使用,java的实现方式)
二、采用Handler的postDelayed(Runnable, long)方法(最简单的android实现)
三、采用Handler与timer及TimerTask结合的方法(比较多的任务时建议使用)

下面逐一介绍:

一、采用Handle与线程的sleep(long)方法

Handler主要用来处理接受到的消息。这只是最主要的方法,当然Handler里还有其他的方法供实现,有兴趣的可以去查API,这里不过多解释。

1. 定义一个Handler类,用于处理接受到的Message。

[java] view plaincopy
  1. Handler handler = new Handler() {  
  2.     public void handleMessage(Message msg) {  
  3.         // 要做的事情  
  4.         super.handleMessage(msg);  
  5.     }  
  6. };  
2. 新建一个实现Runnable接口的线程类,如下: [java] view plaincopy
  1. public class MyThread implements Runnable {  
  2.     @Override  
  3.     public void run() {  
  4.         
  5.         while (true) {  //不断循环,实现每隔10s发送一次Message
  6.             try {  
  7.                 Thread.sleep(10000);// 线程暂停10秒,单位毫秒  
  8.                 Message message = new Message();  
  9.                 message.what = 1;  
  10.                 handler.sendMessage(message);// 发送消息  
  11.             } catch (InterruptedException e) {  
  12.                 // TODO Auto-generated catch block  
  13.                 e.printStackTrace();  
  14.             }  
  15.         }  
  16.     }  
  17. }  
3. 在需要启动线程的地方加入下面语句: [java] view plaincopy
  1. new Thread(new MyThread()).start();  

4. 启动线程后,线程每10s发送一次消息。

二、采用Handler的postDelayed(Runnable, long)方法

这个实现比较简单一些。

1. 定义一个Handler类

[java] view plaincopy
  1. Handler handler=new Handler();  
  2. Runnable runnable=new Runnable() {  
  3.     @Override  
  4.     public void run() {    
  5.         //要做的事情  
  6.         handler.postDelayed(this2000);  
  7.     }  
  8. };  

2. 启动计时器

[java] view plaincopy
  1. handler.postDelayed(runnable, 2000);//每两秒执行一次runnable.  
3. 停止计时器 [java] view plaincopy
  1. handler.removeCallbacks(runnable);   

三、采用Handler与timer及TimerTask结合的方法

1. 定义定时器、定时器任务及Handler句柄

[java] view plaincopy
  1. private final Timer timer = new Timer();  
  2. private TimerTask task;  
  3. Handler handler = new Handler() {  
  4.     @Override  
  5.     public void handleMessage(Message msg) {    
  6.         // 要做的事情  
  7.         super.handleMessage(msg);  
  8.     }  
  9. };  
2. 初始化计时器任务

[java] view plaincopy
  1. task = new TimerTask() {  
  2.     @Override  
  3.     public void run() {  
  4.         // TODO Auto-generated method stub  
  5.         Message message = new Message();  
  6.         message.what = 1;  
  7.         handler.sendMessage(message);  
  8.     }  
  9. };   

3. 启动定时器

[java] view plaincopy
  1. timer.schedule(task, 20002000);   

3. 停止计时器

[java] view plaincopy
  1. timer.cancel();  

简要说一下上面三步提到的一些内容:

1. 定时器任务(TimerTask)顾名思义,就是说当定时器到达指定的时间时要做的工作,这里是想Handler发送一个消息,由Handler类进行处理。

2. java.util.Timer.schedule(TimerTask task, long delay):这个方法是说,dalay/1000秒后执行task.只执行一次。
java.util.Timer.schedule(TimerTask task, long delay, long period):这个方法是说,delay/1000秒后执行task,然后进过period/1000秒再次执行task,这个用于循环任务,执行无数次,当然,你可以用timer.cancel();取消计时器的执行。

每一个Timer仅对应唯一一个线程。
Timer不保证任务执行的十分精确。
Timer类的线程安全的。



Timer有以下几种危险

a. Timer是基于绝对时间的。容易受系统时钟的影响。 如果在任务执行期间,更改了系统时间,那么会导致时间计算不准确问题,导致任务没用按找预定的时间执行。
b. Timer只新建了一个线程来执行所有的TimeTask。所有TimeTask可能会相关影响 。 Timer中的任务是依次执行的,并且它对任务的实时调度并没有保证,因为作为底层的实现依赖于Object.wait(long)方法。只能一次执行一个任务,如果前一个任务没有执行完成,后一个任务是无法并行执行的,只能等待前一个任务执行完成才能执行。也有可能会出现这样的结果,前一个任务执行的时间太长,后几个任务时间短,可能在一个时间段内执行了多个任务,任务又没有按照我们要执行的时间执行。
c. Timer不会捕获TimerTask的异常,只是简单地停止。这样势必会影响其他TimeTask的执行。 

在Android上呢,可以用 java.util.concurrent.ScheduledThreadPoolExecutor也可以用Handler机制做,但是不建议使用Timer

ScheduledThreadPoolExecutor采用相对时间,用线程池来执行TimerTask,会出来TimerTask异常。JDK1.5 API中关于这个类的详细介绍: 
 "可另行安排在给定的延迟后运行命令,或者定期执行命令。需要多个辅助线程时,或者要求 ThreadPoolExecutor 具有额外的灵活性或功能时,此类要优于 Timer。 
    一旦启用已延迟的任务就执行它,但是有关何时启用,启用后何时执行则没有任何实时保证。按照提交的先进先出 (FIFO) 顺序来启用那些被安排在同一执行时间的任务。 虽然此类继承自 ThreadPoolExecutor,但是几个继承的调整方法对此类并无作用。特别是,因为它作为一个使用 corePoolSize 线程和一个*队列的固定大小的池,所以调整 maximumPoolSize 没有什么效果。"  


我们使用Timer或者handler的时候会发现,delay时间并没有那么准。如果我们需要一个严格准时的定时操作,那么就要用到AlarmManager,AlarmManager对象配合Intent使用,可以定时的开启一个Activity,发送一个BroadCast,或者开启一个Service.

Timer类的还有一个明显的短板:不适用于需要长期在后台运行的定时任务。为了让手机更耐用,每个手机都有自己的休眠策略,Android手机就会在长时间不操作的情况下自动让CPU进入睡眠状态,这可能导致Timer中的定时任务无法正常运行。而Alarm机制则不会出现这种情况,因为它有唤醒CPU的功能,即可以保证每次需要执行定时任务的时候CPU都能正常工作。

长期在后台执行定时任务:

<pre name="code" class="javascript">public class LongRunningService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(){
<span></span>@Override
<span></span>public void run() {
<span></span>//执行具体的定时任务
<span></span>}
<span></span>};
<span></span>AlarmManager alarmManager= (AlarmManager) getSystemService(ALARM_SERVICE);
<span></span>int anHour=60*60*1000;//一小时
long triggerAtTime= SystemClock.elapsedRealtime()+anHour;
Intent i=new Intent(LongRunningService.this,AlarmReceiver.class);
PendingIntent pi=PendingIntent.getBroadcast(this,0,i,0);
//ELAPSED_REALTIME_WAKEUP:让定时任务的触发时间从系统开机开始算起,并会唤醒CPU
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime,pi);

return super.onStartCommand(intent,flags,startId);
}

private class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent i=new Intent(context,LongRunningService.class);
context.startService(i);
}
}
}


 

在onStartCommand中开启一个子线程,在里面执行具体操作。然后用getSystemService得到一个AlarmManager,然后用set设置定时任务,启动广播接收器。一小时后,在广播接收器中的onReceive就会执行,然后在里面启动Service,这样就形成了一个循环,LongRunningService会一小时就启动一次。

或者用setRepeating:

public static void sendUpdateBroadcastRepeat(Context ctx){
Intent intent =new Intent(ctx, UpdateReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(ctx, 0, intent, 0);
//开始时间
long firstime=SystemClock.elapsedRealtime();
AlarmManager am = (AlarmManager) ctx.getSystemService(ALARM_SERVICE);
  //60秒一个周期,不停的发送广播
am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, firstime, 60*1000, pendingIntent);
}