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

  • Post author:
  • Post category:其他


前言

最近项目上有这么一个需求,实时监控车辆信息,要求每隔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);
}
......
@Override
public 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);

......

@Override
public 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