您的位置:首页 > 移动开发 > Android开发

android的定时任务

2015-11-28 22:03 585 查看
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

Handler handler = new Handler() {  

    public void handleMessage(Message msg) {  

        // 要做的事情  

        super.handleMessage(msg);  

    }  

};  

2. 新建一个实现Runnable接口的线程类,如下:

[java] view
plaincopy

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();  

            }  

        }  

    }  

}  

3. 在需要启动线程的地方加入下面语句:

[java] view
plaincopy

new Thread(new MyThread()).start();
 

4. 启动线程后,线程每10s发送一次消息。
二、采用Handler的postDelayed(Runnable, long)方法

这个实现比较简单一些。

1. 定义一个Handler类

[java] view
plaincopy

Handler handler=new Handler();  

Runnable runnable=new Runnable() {  

    @Override  

    public void run() {
   

        //要做的事情  

        handler.postDelayed(this, 2000);  

    }  

};  

2. 启动计时器

[java] view
plaincopy

handler.postDelayed(runnable, 2000);//每两秒执行一次runnable.  

3. 停止计时器

[java] view
plaincopy

handler.removeCallbacks(runnable);   

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

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

[java] view
plaincopy

private final Timer timer = new Timer();  

private TimerTask task;  

Handler handler = new Handler() {  

    @Override  

    public void handleMessage(Message msg) {
   

        // 要做的事情  

        super.handleMessage(msg);  

    }  

};  

2. 初始化计时器任务

[java] view
plaincopy

task = new TimerTask() {  

    @Override  

    public void run() {  

        // TODO Auto-generated method stub  

        Message message = new Message();  

        message.what = 1;  

        handler.sendMessage(message);  

    }  

};   

3. 启动定时器

[java] view
plaincopy

timer.schedule(task, 2000, 2000);   

3. 停止计时器

[java] view
plaincopy

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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: