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。
- Handler handler = new Handler() {
- public void handleMessage(Message msg) {
- // 要做的事情
- super.handleMessage(msg);
- }
- };
- public class MyThread implements Runnable {
- @Override
- public void run() {
- while (true) { //不断循环,实现每隔10s发送一次Message
- try {
- Thread.sleep(10000);// 线程暂停10秒,单位毫秒
- Message message = new Message();
- message.what = 1;
- handler.sendMessage(message);// 发送消息
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- }
- new Thread(new MyThread()).start();
4. 启动线程后,线程每10s发送一次消息。
二、采用Handler的postDelayed(Runnable, long)方法
这个实现比较简单一些。
1. 定义一个Handler类
- Handler handler=new Handler();
- Runnable runnable=new Runnable() {
- @Override
- public void run() {
- //要做的事情
- handler.postDelayed(this, 2000);
- }
- };
2. 启动计时器
- handler.postDelayed(runnable, 2000);//每两秒执行一次runnable.
- handler.removeCallbacks(runnable);
三、采用Handler与timer及TimerTask结合的方法
- private final Timer timer = new Timer();
- private TimerTask task;
- Handler handler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- // 要做的事情
- super.handleMessage(msg);
- }
- };
- task = new TimerTask() {
- @Override
- public void run() {
- // TODO Auto-generated method stub
- Message message = new Message();
- message.what = 1;
- handler.sendMessage(message);
- }
- };
3. 启动定时器
- timer.schedule(task, 2000, 2000);
3. 停止计时器
- 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有以下几种危险
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);
}