定时任务实现总结

在Android中这算是一个常用的功能了,,有兴趣一起来探讨下可以

Android消息机制

首先来了解一下Android的消息处理机制

即Handlerd的运行机制,handler的运行需要底层的MessageQueue和Looper的支撑。MessageQueue(消息队列),它的内部存储了一些消息,以队列的形式对外提供插入和删除的操作(实际为单链表存储)。Looper(消息循环),配合MessageQueue实现实现消息的不断入队和出队工作。

一个关系图:

[置顶] Android 定时任务的多种实现方式_第1张图片

通过Handler可以很容易将任务切换到其他线程中执行,以减少主线程的负担,因此Handler常用来进行UI更新。这里只是简单的进行一些概述。对应handler还不清楚的强烈建议参考以下博客:
Android 异步消息处理机制
Android AsyncTask完全解析

当然,现在已经有更好的消息处理办法了,了解handler和Asynctask可以更好的理解Android内部消息的处理机制。
推荐:EventBus,高度解耦,代码简洁明了,有兴趣的可以自行参考使用。

1.采用Handle与线程的sleep(long)方法

1) 定义一个Handler类,用于处理接受到的Message。

Handler handler = new Handler() {      public void handleMessage(Message msg) {          // 要做的事情         super.handleMessage(msg);      }  };  

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

public class MyThread implements Runnable {      @Override      public void run() {          // TODO Auto-generated method stub         while (true) {              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) 在需要启动线程的地方加入下面语句:

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

分析:纯正的java原生实现,在sleep结束后,并不能保证竞争到cpu资源,这也就导致了时间上必定>=10000的精度问题。

2.采用Handler的postDelayed(Runnable, long)方法

1)定义一个Handler类

Handler handler=new Handler();  Runnable runnable=new Runnable() {      @Override      public void run() {          // TODO Auto-generated method stub         //要做的事情         handler.postDelayed(this, 2000);      }  };  

2) 启动与关闭计时器

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

分析:嗯,看起蛮不错,实现上也简单了,和sleep想必还不会产生阻塞,注意等待和间隔的区别。

3.采用Handler与timer及TimerTask结合的方法

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

private final Timer timer = new Timer();  private TimerTask task;  Handler handler = new Handler() {      @Override      public void handleMessage(Message msg) {          // TODO Auto-generated method stub         // 要做的事情         super.handleMessage(msg);      }  };  

2) 初始化计时器任务

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, 3000);   
timer.cancel();  

此外,Timer也可以配合runOnUiThread实现,如下

    private TimerTask mTimerTask = new TimerTask() {        @Override        public void run() {            runOnUiThread(new Runnable() {                @Override                public void run() {                    //处理延时任务                }            });        }    };

分析:timer.schedule(task, 2000, 3000);意思是在2秒后执行第一次,之后每3000秒在执行一次。timer不保证精确度且在无法唤醒cpu,不适合后台任务的定时。

采用AlarmManger实现长期精确的定时任务

AlarmManager的常用方法有三个:

  • set(int type,long startTime,PendingIntent pi);//一次性
  • setExact(int type, long triggerAtMillis, PendingIntent operation)//一次性的精确版
  • setRepeating(int type,long startTime,long intervalTime,PendingIntent
    pi);//精确重复
  • setInexactRepeating(int type,long startTime,long
    intervalTime,PendingIntent pi);//非精确,降低功耗

type表示闹钟类型,startTime表示闹钟第一次执行时间,long intervalTime表示间隔时间,PendingIntent表示闹钟响应动作

对以上各个参数的详细解释
闹钟的类型:

  • AlarmManager.ELAPSED_REALTIME:休眠后停止,相对开机时间
  • AlarmManager.ELAPSED_REALTIME_WAKEUP:休眠状态仍可唤醒cpu继续工作,相对开机时间
  • AlarmManager.RTC:同1,但时间相对于绝对时间
  • AlarmManager.RTC_WAKEUP:同2,但时间相对于绝对时间
  • AlarmManager.POWER_OFF_WAKEUP:关机后依旧可用,相对于绝对时间

绝对时间:1970 年 1月 1 日 0 点

startTime:
闹钟的第一次执行时间,以毫秒为单位,一般使用当前时间。

  • SystemClock.elapsedRealtime():系统开机至今所经历时间的毫秒数
  • System.currentTimeMillis():1970 年 1 月 1 日 0 点至今所经历时间的毫秒数

intervalTime:执行时间间隔。

PendingIntent :
PendingIntent用于描述Intent及其最终的行为.,这里用于获取定时任务的执行动作。
详细参考译文:PendingIntent

利用AlarmManger+Service+BarocastReceiver实现5s一次打印操作

服务类:

public class HorizonService extends Service {    @Override    public IBinder onBind(Intent intent) {        return null;    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        new Thread(new Runnable() {            @Override            public void run() {                Log.d("TAG", "打印时间: " + new Date().                        toString());            }        }).start();        AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);        int five = 5000; // 这是5s        long triggerAtTime = SystemClock.elapsedRealtime() + five;        Intent i = new Intent(this, AlarmReceiver.class);        PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0);        manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);        return super.onStartCommand(intent, flags, startId);    }}

广播接受器

public class AlarmReceiver extends BroadcastReceiver {    @Override    public void onReceive(Context context, Intent intent) {        Intent i = new Intent(context, HorizonService.class);        context.startService(i);    }}

启动定时任务:

Intent intent = new Intent(this,HorizonService.class);startService(intent);

效果Demo:
[置顶] Android 定时任务的多种实现方式_第2张图片

本例通过广播接收器和服务的循环调用实现了无限循环的效果,当然,你也可以直接利用setRepeating实现同样的效果。

注意:不要忘了在manifest文件中注册服务和广播接收器。

AlarmManager的取消方法:AlarmManger.cancel();

分析:该方式可唤醒cpu甚至实现精确定时,适用于配合service在后台执行一些长期的定时行为。

本文总结:不建议使用第一种方式,短期的定时任务推荐第二、三种方式实现,长期或者有精确要求的定时任务则可以配合Service在后台执行。

有其他疑问可下方提出或者直接链接官方文档AlarmService

更多相关文章

  1. Android中的消息机制
  2. Android消息机制Looper与VSync的传播
  3. [置顶] Android学习记录(6)—将java中的多线程下载移植到Android中
  4. Android学习记录(6)—将java中的多线程下载移植到Android中(即多线
  5. Android 当前时间的获取
  6. 关于android contentprovider 多线程读取问题
  7. 理解Android UI线程
  8. Android查看手机线程指令

随机推荐

  1. 一个很有深度的Android Blog
  2. Android 新手入门指导
  3. android 签名
  4. Android studio生成APK打包,修改生成APK的
  5. Android 支持的文件类型
  6. Android控件属性大全
  7. Step Detector and Step Counter Sensors
  8. Android——PopupWindow
  9. lua学习笔记 1 android 调用Lua, Lua脚本
  10. AM335X Starter Kit Android 开发环境搭