Android中的定时任务一般有两种实现方式,一种是使用JavaAPI里的Timer类,另一种是使用android的Alarm机制。

这两种方式在多数情况下都能实现类似的效果,但Timer有一个明显的短板,它并不太适用与那些需要长期在后台运行的定时任务。As we know,为了能让电池更加耐用,每种手机都会有自己的休眠策略:比如手机不用的时候智能的断开wifi连接,根据光纤强弱自动调节屏幕亮度,根据手机长时间无操作时自动的让CPU进入到休眠状态等,当进入休眠状态时,这就有可能导致Timer中的定时任务无法正常运行。而Alarn机制则不存在这种情况,它具有唤醒CPU的功能,即可以保证每次需要执行定时任务的时候CPU都能正常工作。需要注意的是,这里的唤醒CPU和唤醒屏幕不是同一个概念,不能混淆。


Timer类

使用Timer及TimerTask可以在某个时间执行某一个任务。
 Timer是一个普通的类,有几个重要的方法。而TimerTask是一个抽象类,要使用的时候必须实现它的Run方法。
TimerTask的run方法类似于线程的run方法。 我们使用Timer创建一个他的对象,然后使用这对象的schedule方法来完成这种间隔的操作。
 
schedule方法有三个参数
第一个参数就是TimerTask类型的对象,我们实现TimerTask的run()方法就是要周期执行的一个任务;
第二个参数有两种类型,第一种是long类型,表示多长时间后开始执行,另一种是Date类型,表示从那个时间后开始执行;
第三个参数就是执行的周期,为long类型。

schedule方法还有一种两个参数的执行重载,第一个参数仍然是TimerTask,第二个表示为long的形式表示多长时间后执行一次,为Date就表示某个时间后执行一次。

 
Timer就是一个线程,使用schedule方法完成对TimerTask的调度,多个TimerTask可以共用一个Timer,也就是说Timer对象调用一次schedule方法就是创建了一个线程,并且调用一次schedule后TimerTask是无限制的循环下去的,使用Timer的cancel()停止操作。当然同一个Timer执行一次cancel()方法后,所有Timer线程都被终止。
 
 //true 说明这个timer以daemon方式运行(优先级低,程序结束timer也自动结束)
java.util.Timer timer = new java.util.Timer(true);
TimerTask task = new TimerTask() {
public void run() {
//每次需要执行的代码放到这里面。
}
}; //以下是几种调度task的方法: //time为Date类型:在指定时间执行一次。
timer.schedule(task, time); //firstTime为Date类型,period为long,表示从firstTime时刻开始,每隔period毫秒执行一次。
timer.schedule(task, firstTime, period); //delay 为long类型:从现在起过delay毫秒执行一次。
timer.schedule(task, delay); //delay为long,period为long:从现在起过delay毫秒以后,每隔period毫秒执行一次。
timer.schedule(task, delay, period);

Alarm机制

.首先获取到AlarmManager的实例。
AlarmManager manager = (AlarmManager)getSystemService(Context.ALARM_SERVICE)
然后调用AlarmManager的set()方法就可以设置一个定时任务了。

long triggerAtTime = SystemClock.elapsedRealtime() +  * ;

manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtime,pendingIntent);

set()方法中第一个参数是一个整型参数,用于指定AlarmManager的工作类型,有4种值可选,分别是ELAPSED_REALTIME、ELAPSED_REALTIME_WAKEUP、RTC和RTC_WAKEUP。

ELAPSED_REALTIME:表示让定时任务的触发时间从系统开机开始算起,但不会唤醒CPU。

ELAPSED_REALTIME_WAKEUP:同样表示让定时任务的触发时间从系统开机时间开始算起,但会唤醒CPU。

RTC:表示让定时任务的触发时间从1970年1月1日0点开始算起,但不会唤醒CPU。

RTC_WAKEUP:表示让定时任务的触发时间从1970年1月1日0点开始算起,但会唤醒CPU。

使用SystemClock.elapsedRealtime()方法可以获取到系统开机至今所经历时间的毫秒数。
使用SystemClock.currentTimeMills()方法可以获取到1970年1月1日0点至今所经历时间的毫秒数。
 
第二个参数是定时任务的触发时间,以毫秒为单位。
若第一个参数使用的是ELAPSED_REALTIME或是ELAPSED_REALTIME_WAKEUP那么这里传入开机至今的时间再加上延迟执行的时间。
若第一个参数使用的是RTC或是RTC_WAKEUP,那么这里传入1970年1月1日0点至今的时间再加上延迟执行的时间。
 
第三个参数是PendingIntent。
pendingIntent是一种特殊的Intent。主要的区别在于Intent的执行立刻的,而pendingIntent的执行不是立刻的。pendingIntent执行的操作实质上是参数传进来的Intent的操作,但是使用pendingIntent的目的在于它所包含的Intent的操作的执行是需要满足某些条件的。主要的使用的地方和例子:通知Notificatio的发送,短消息SmsManager的发送和警报器AlarmManager的执行等等。

PendingIntent中

getActivity(Context, int, Intent, int) 跳转到一个activity组件
getBroadcast(Context, int, Intent, int) 发送一个广播组件
getService(Context, int, Intent, int) 启动一个服务组件

这里我们一般会调用getService()方法或者getBroadcast()方法来获取一个能够执行服务或者广播的PendingIntent。这样当定时任务被触发的时候。服务的onStartCommand()方法或者广播的onReceive()方法就可以得到执行。

如果想要实现一个长时间在后台定时运行的服务该怎么做呢?

只需要定义一个服务,并将触发定时任务的代码写到onStartCommand()方法中。

 @Override
public int onStartCommand(Intent intent,int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
//在这里执行具体逻辑
}
}).start();
AlarmManager manager = (AlarmManager)getSystemService(ALARM_SERVICE);
int anHour = * * ; //这是一小时的毫秒数。
long triggerAtTimer = SystemClock.elapsedRealtime() + anHour;
Intent i = new Intent(this,LongRunningService.class);
PendingIntent pi = PendingIntent.getService(this,,i,);
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTimer,pi);
return super.onStartCommand(intent,flags,startId);
}

我们首先是在onStartCommand()方法中开启了一个子线程,这样就可以在这里执行具体的逻辑操作了,之所以要在子线程里执行逻辑操作,是因为逻辑操作也是需要耗时的,如果放在主线程里执行可能会对

定时任务的准确性造成轻微的影响。

创建线程代码之后,先是获取到了AlarmManager的实例, 然后定义任务的触发时间为一个小时后,再使用PendingIntent指定处理定时任务的服务为LongRunningService,最后调用set()方法完成设定。

一旦启动LongRunningService,就会在onStartCommand()方法里设定了一个定时任务,这样一个小时后,将会再次启动LongRunningService。从而形成一个永久的循环,保证LongRunningService的onStartCommand()方法可以每隔一个小时就执行一次。

如果想要启动定时服务的时候调用以下代码即可。

 Intent intent = new Intent(context,LongRunningService.class);
context.startService(intent);

需要注意的是,从Android4.4开始,Alarm任务的触发时间将会变得不准确,有可能会延迟一段时间后任务才能执行,这是系统在耗电性方面的优化,系统会自动检测目前有多少Alarm任务存在,然后将触发时间相近的几个任务放在一起执行,这样就可以大幅度减少CPU被唤醒的次数,从而减少耗电量。

如果要求Alarm任务的执行时间必须准确无误,只需要使用AlarmManager的setExact()方法来替代set()方法。就基本上可以保证任务能够准时执行了。

 
 

Android下的定时任务的更多相关文章

  1. Android下/data/data/<package_name>/files读写权限

    今天将更新模块拿到android上面测试的时候,发现在创建writablepath.."upd/"目录的时候出现Permission Denied提示BTW:我使用的是lfs来创建 ...

  2. Android下Cocos2d创建HelloWorld工程

    最近在搭建Cocos2d的环境,结果各种问题,两人弄了一天才能搞好一个环境-! -_-!! 避免大家也可能会遇到我这种情况,所以写一个随笔,让大家也了解下如何搭建吧- 1.环境安装准备 下载 tadp ...

  3. Android下读取logcat的信息

    有时我们需要在程序执行进程中遇到一些异常,需要收集一logcat的信息,android下就可以使用以下方法获取: private static String getLogcatInfo(){ Stri ...

  4. Android下OpenCV的环境搭建

    目录(?)[-] 前言 系统环境 相关工具 Android ADT环境搭建 Android SDK环境变量的配置 Android NDK的安装与配置 OpenCV for Android 环境搭建 基 ...

  5. Android下添加新的自定义键值和按键处理流程

            Android下添加新的自定义键值和按键处理流程     说出来不怕大家笑话,我写这篇博客的原因在于前几天去一个小公司面试Android系统工程师,然后在面试的时候对方的技术总监问了我 ...

  6. Android下的数据储存方式(三)

      Android下最好的数据储存方式:关系型数据库sqlite.   数据库的创建:使用SqliteOpenHelper类 结合SqliteOpenHelper类和SQLiteDatabase类的帮 ...

  7. 无废话Android之android下junit测试框架配置、保存文件到手机内存、android下文件访问的权限、保存文件到SD卡、获取SD卡大小、使用SharedPreferences进行数据存储、使用Pull解析器操作XML文件、android下操作sqlite数据库和事务(2)

    1.android下junit测试框架配置 单元测试需要在手机中进行安装测试 (1).在清单文件中manifest节点下配置如下节点 <instrumentation android:name= ...

  8. windows 下的定时任务

    linux 下的定时任务是crontab 以前都是linux的定时任务,这次在windows做了定时任务,简单记录一下 windows 2008下的定时任务配置: 控制面板->管理工具-> ...

  9. 转:RTC搭建android下三层应用程序访问服务器MsSql-客户端

    原文:http://www.cnblogs.com/delphi007/p/3346084.html android下stringgrid已知问题: 通过点击时获取对应行的值有问题,在win下调试正常 ...

随机推荐

  1. Linux] Git: push 出错的解决 master -> master (branch is currently checked out)

      在使用Git Push代码到数据仓库时,提示如下错误: [remote rejected] master -> master (branch is currently checked out ...

  2. 你知道你对 JSON Web Token 的认识存在误解吗

    1.前言 JSON Web Token (JWT) 其实目前已经广为软件开发者所熟知了,但是 JOSE (Javascript Object Signing and Encryption) 却鲜有人知 ...

  3. Juptyer中的图表显示参数

    from pylab import * import seaborn as sns # 图表显示参数 from IPython.display import display %matplotlib i ...

  4. tensorflow(二)

    使用图来表示计算任务 在被称之为session的上下文中执行图 使用tensor表示数据 通过变量来维护状态 使用feed和fetch可以为任意的操作复制或者从其中获取数据 tensorflow是一个 ...

  5. 能够伪装为 win 10 的 kali 体验与中文设置

    前言 作为习惯性捣鼓各类操作系统,时长也会使用 Kali 系统,之前看到有新的版本发行 传闻这个版本和之前的版本在系统界面和壁纸上都做了更新,还能一键设置 win 10 的系统界面 对此决定下载体验一 ...

  6. Codeforces Round #599 (Div. 2)D 边很多的只有0和1的MST

    题:https://codeforces.com/contest/1243/problem/D 分析:找全部可以用边权为0的点连起来的全部块 然后这些块之间相连肯定得通过边权为1的边进行连接 所以答案 ...

  7. 混合欧拉回路poj 1637 Sightseeing tour

    把该图的无向边随便定向,计算每个点的入度和出度.如果有某个点出入度之差为奇数,那么肯定不存在欧拉回路.因为欧拉回路要求每点入度 = 出度,也就是总度数为偶数,存在奇数度点必不能有欧拉回路: 好了,现在 ...

  8. day28-黏包现象

    #黏包现象:信息还没接收完,下一次接收的时候一下子接收好几条黏在一起的信息. #黏包现象的原因:接收端不知道发送端发送的数据的长度.recv(字节数小了). # 第一次无法接收完就缓存起来,下一次接收 ...

  9. Listening-lecture|主旨题|术语解释|举例原则|Crash course 哔哩哔哩

    Listening-lecture: Major topic: SP1---detail---detail---detail SP2---detail---detail---detail Crash ...

  10. spring-mvc基于xml的配置

    配置web.xml <!--配置spring-MVC拦截--> <servlet> <servlet-name>DispatcherServlet</serv ...