android 定时任务(TimerTask和Handler对比)

来源:互联网 发布:win10网络图标不能运行 编辑:程序博客网 时间:2024/06/05 22:41

前言

最近项目上有这么一个需求,实时监控车辆信息,要求每隔3秒钟刷新一次地图上的车辆位置信息。我的想法是先定时从服务端获取数据存储到SharedPreferences中,然后再定时从SharedPreferences中获取数据显示到地图。对这个逻辑我不满意,但是一时也找不到别的方法,望大神指教。

在使用定时任务的时候,最开始想到的是Timer。无意中看到一种Handler加Runnable方法,觉得还是有必要记录一下。

Timer方法

Timer一般结合TimerTask使用。先看TimerTask,它是一个抽象类,里面有一个run()方法。

public abstract class TimerTask implements Runnable {

....../** * The action to be performed by this timer task. */public abstract void run();......

}
查看TimerTask的源码,可以看到TimerTask其实就是实现了Runnable方法,也就是说,通过Timer执行定时任务,其实就是通过一个线程做到的。

再看Timer,它其实就是一个线程管理器,通过schedule方法来开启一个线程,并实现任务调度。Timer工作机制下篇文章再撸,这里简单理解并记录使用方法。

/**
* A facility for threads to schedule tasks for future execution in a
* background thread. Tasks may be scheduled for one-time execution, or for
* repeated execution at regular intervals.

……

  • After the last live reference to a Timer object goes away

  • and all outstanding tasks have completed execution, the timer’s task
  • execution thread terminates gracefully (and becomes subject to garbage
  • collection). However, this can take arbitrarily long to occur. By
  • default, the task execution thread does not run as a daemon thread,
  • so it is capable of keeping an application from terminating. If a caller
  • wants to terminate a timer’s task execution thread rapidly, the caller
  • should invoke the timer’s cancel method.

    ……

  • This class is thread-safe: multiple threads can share a single

  • Timer object without the need for external synchronization.
    *
  • This class does not offer real-time guarantees: it schedules

  • tasks using the Object.wait(long) method.

    ……
    */
    截几段源码注释

当所有TimerTask任务完成,并且Timer引用为空的时候,会被GC回收。
一般程序退出时,TimerTask任务会跟随着终止,主动结束则用Timer的cancel方法。
多个线程可共用一个Timer,也就是多个TimerTask可以共用一个Timer。
Timer不能保证实时任务,所有的任务都得等待调度。
说人话,来个比喻。Timer是一个码头大哥(简称T老大),手底下有一帮小弟(Thread,简称w)跟一个管家(schedule,简称S管家)。TimerTask是一个商人(简称K老板),手底下有一帮业务经理(实例化的timerTask,负责某个具体的任务,简称p经理)。他有一批货要在码头卸载。于是乎,K老板找到了T老大。

商人:T老大,我有一批货要在贵码头卸载,能不能帮帮忙,价钱好商量!
老大:路过的都是朋友,好说!好说!
商人:我这批货很急,能不能立马就下?(想要实时)
老大:先来后到,这是规矩啊!能不能马上卸货,就要看K老板您的运气了。要是运气好,就能立马卸货,要是运气不好,等个一年半载都有可能啊。哈哈哈、、、还望海涵!(无法保证实时)
商人:那就只能等T老大的好消息了。
商人就只能在码头排队等候了。某天,轮到K老板卸货了。

老大:K老板久等了!接下来在下静候吩咐。
商人:岂敢!只是鄙人的货有点杂,需要送到不同的地方,还要麻烦T老大啊。
老大:小事一桩!只管说给我的S管家听,保证K老板满意!(多个任务可共用一个Timer)
管家:K老板只管吩咐,老夫一定保质保量完成。
商人:那就多谢S管家!具体的任务我让我的经理们给您汇报。

得到老板的指示,S管家跟p经理们开始干活了。一个经理代表一个具体的任务,一个w代表一个线程。

schedule的四个方法

schedule(task, date),指定时间执行一次
经理1:S管家,这批布匹要晚上6点往城东的布衣店送去。
管家:w1,你来,把这个送到城东的布衣店去。晚上6点就去。
schedule(task, delay), 从现在起,delay毫秒后,执行一次
经理2:S管家,这批水果过两个小时往城东的水果店送去。
管家:w2,你来,两个小时后,开始往城东的水果店送。
schedule(task, firstTime, period),firstTime时刻开始,每隔period毫秒执行一次
经理3:S管家,这批黄金要明天凌晨4点开始,每隔1小时往城南的金铺送一次。
管家:w3,你来,明天早起,凌晨4点开始干活,没喊停,就一直干。
schedule(task, delay, period),现在开始,delay毫秒后,每隔period毫秒执行一次
经理4:S管家,这批书要往城北的陈家送去,2个小时后他们家才有人,每隔一个小时送一次。
管家:w4,你来,吃点东西,2个小时后往陈家送书,一个小时送一次,没喊停,就一直干。

若干天后,S管家没有收到K老板的支付款,便向T老大汇报。

管家:老大,K老板不守信用啊,款项没有及时到账,如何处理?
老大:不守规矩,以后不跟他玩了,让他找别人。(cancel方法,主动结束timerTask)
商人:那我不得重新找码头了?T老大,通融通融吧。
老大:没门儿!(一旦取消,要想继续只能重新new一个Timer)
待K老板交足款项后,只能重新排队等待卸货。某天,海啸来了,码头被毁(程序退出),一切都没了(timerTask被动结束)。
啰嗦了半天,不知道说明白没有。还是上代码,直观显示。

......private Timer timer = new Timer();......private void useTimer() {    //多个任务可共享一个timer    timer.schedule(new MarkerTask(), 1000, 2000);    timer.schedule(new MapTask(), 1000, 3000);}......@Overridepublic void onClick(View v) {    switch (v.getId()) {        case R.id.stopRefresh://取消定时任务            Log.d("MainActivity", "onClick: stopRefresh");            timer.cancel();            break;        case R.id.continueRefresh://继续定时任务            Log.d("MainActivity", "onClick: continueRefresh");            Timer timer1 = new Timer();            timer1.schedule(new MarkerTask(), 1000, 2000);            timer1.schedule(new MapTask(), 1, 3000);            break;        default:            break;    }}......private class MapTask extends TimerTask {    @Override    public void run() {        refreshMapViewDetail();    }}private class MarkerTask extends TimerTask {    @Override    public void run() {        randomMarkerDetail();    }}......

Handler加Runnable方法

这个方法的核心思想就是在Runnable内部,将本runnable继续插入主线程队列中。理解了Handler的工作机制,这个方法就更好理解,这里直接贴代码。

......    mapRefreshRun = new Runnable() {        @Override        public void run() {            //更新地图上的数据            refreshMapViewDetail();            //将本runnable继续插入主线程中,再次执行。            handler.postDelayed(this, 3000);        }    };    handler.postDelayed(mapRefreshRun, 1);......@Overridepublic void onClick(View v) {    switch (v.getId()) {        case R.id.stopRefresh:            Log.d("MainActivity", "onClick: stopRefresh");            handler.removeCallbacks(mapRefreshRun);            break;        case R.id.continueRefresh:            Log.d("MainActivity", "onClick: continueRefresh");            handler.postDelayed(mapRefreshRun, 1);            break;        default:            break;    }}

……

要取消任务直接用Handler的removeCallbacks方法,要继续任务,则将任务继续插入主线程中。

总结

Timer方法是新建子线程,在子线程中执行想要的动作,故不可以直接更改UI;
Handler方法是在主线程中,插入Runnable,可以直接更改UI。对界面的操作,比如点击、滑动这些都是要在主线程中排队进行的,如果我们这个定时任务的period太短,比如设为0,会否影响用户的操作响应速度呢?在这个简单的Demo中,试了下,没有太明显的感觉,但这个问题还是要留意。

转自:http://www.jianshu.com/p/45fe8b5ca21e

原创粉丝点击