Android定时任务

前言

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

  ......

 * <p>After the last live reference to a <tt>Timer</tt> object goes away
 * <i>and</i> 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 <i>daemon thread</i>,
 * 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 <tt>cancel</tt> method.

  ......

 * <p>This class is thread-safe: multiple threads can share a single
 * <tt>Timer</tt> object without the need for external synchronization.
 *
 * <p>This class does <i>not</i> offer real-time guarantees: it schedules
 * tasks using the <tt>Object.wait(long)</tt> 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中,试了下,没有太明显的感觉,但这个问题还是要留意。

本Demo的代码请移步:GitHub

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • 参考:Android中定时器的3种实现方法 Thread.sleep java的方式,不推荐sleep使线程暂停,...
    zeo朱子宥阅读 903评论 2 0
  • 本章目录 Part One:Timer Part Two:AlarmManager Android中有很多种实现定...
    黄烨1121阅读 8,942评论 0 4
  • Android中几种定时器的总结和比较 - 爱悠闲 采用Handler与线程的sleep(long)方法 采用H...
    HEHE_fang阅读 349评论 0 0
  • Timer 定时器相信都不会陌生,之所以拿它来做源码分析,是发现整个控制流程可以体现很多有意思的东西。 在业务开发...
    石先阅读 6,328评论 2 13
  • 期待远方 那里充满希望 美好总是离得很远 眼前茫茫一片 不知还有多久 能够到达 匆忙的步伐下 我们信心满怀 也许荆...
    我是文化人哦阅读 193评论 0 0