Android定时任务的应用及实现
实现正定时的五种方法
方法1:通过Handler + Thread 的方式。代码如下。
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final int TIMER = 999;
private static boolean flag = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setTimer();
}
private void setTimer(){
new Thread(new Runnable() {
@Override
public void run() {
while (flag){
try {
Thread.sleep(1000); //休眠一秒
mHanler.sendEmptyMessage(TIMER);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
private Handler mHanler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case TIMER:
//去执行定时操作逻辑
break;
default:
break;
}
}
};
private void stopTimer(){
flag = false;
}
@Override
protected void onDestroy() {
super.onDestroy();
stopTimer();
}
}
这是比较容易想到的方法,值得注意的是:如果在此Activity关闭之前,必须要终止线程内的循环,否则就会造成内存泄露。
方法2:通过Handler + Message的方式。代码如下。
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final int TIMER = 999;
private static boolean flag = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setTimer();
}
private void setTimer(){
Message message = mHandler.obtainMessage(TIMER); // Message
mHandler.sendMessageDelayed(message, 1000);
}
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case TIMER:
//在这里去执行定时操作逻辑
if (flag) {
Message message = mHandler.obtainMessage(TIMER);
mHandler.sendMessageDelayed(message, 1000);
}
break;
default:
break;
}
}
};
private void stopTimer(){
flag = false;
}
}
这个方法来实现定时其实还是有些缺陷的,主要的问题不是看起来像是死循环,而是在执行定时操作之后才可以进行下一次定时启动,如果此操作是耗时操作了,必定会延后下一秒的启动。所以这个方法定时严格来说不精确,不推荐。
方法3:通过Handler + Runnable的方式。代码如下。
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static boolean flag = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setTimer();
}
private void setTimer(){
mHandler.postDelayed(runnable, 1000);
}
private Handler mHandler = new Handler();
Runnable runnable = new Runnable() {
@Override
public void run() {
//在这里执行定时需要的操作
if (flag) {
mHandler.postDelayed(this, 1000);
}
}
};
private void stopTimer(){
flag = false;
}
}
此方法和方法2很相似,缺点也相似,优点嘛~~简单!
方法4:通过Handler + TimerTask的方式。代码如下。
MainActivity.java
private static final int TIMER = 999;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setTimer();
}
private void setTimer(){
timer.schedule(task, 1000, 1000); // timeTask
}
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case TIMER:
//在此执行定时操作
break;
default:
break;
}
}
};
Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
mHandler.sendEmptyMessage(TIMER);
}
};
private void stopTimer(){
timer.cancel();
task.cancel();
}
@Override
protected void onDestroy() {
super.onDestroy();
stopTimer();
}
}
这里引用了timer和task两个新的类,配合handler使用是目前比较多的方式,but我也不推荐,因为下面这个是我推荐的封装方法。
MyTimeTask.java
public class MyTimeTask {
private Timer timer;
private TimerTask task;
private long time;
public MyTimeTask(long time, TimerTask task) {
this.task = task;
this.time = time;
if (timer == null){
timer=new Timer();
}
}
public void start(){
timer.schedule(task, 0, time);//每隔time时间段就执行一次
}
public void stop(){
if (timer != null) {
timer.cancel();
if (task != null) {
task.cancel(); //将原任务从队列中移除
}
}
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final int TIMER = 999;
private MyTimeTask task;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setTimer();
}
private void setTimer(){
task =new MyTimeTask(1000, new TimerTask() {
@Override
public void run() {
mHandler.sendEmptyMessage(TIMER);
//或者发广播,启动服务都是可以的
}
});
task.start();
}
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case TIMER:
//在此执行定时操作
break;
default:
break;
}
}
};
private void stopTimer(){
task.stop();
}
@Override
protected void onDestroy() {
super.onDestroy();
stopTimer();
}
}
单独封装出来的定时任务工具,引入方式简单,方便取消停止定时任务。简单来说优点就是:把工具类直接复制粘贴,然后简单调用就能用!
方法5:采用AlarmManger实现长期精确的定时任务。
此方法设计需要细说的点有很多,不再列代码,最好是各位亲自去写一写,对此方法印象会深一些,可以给出一个非常常见的应用场景:每隔6个小时,检查一遍数据库内容,查看数据是否有变化,如果有则弹出显示框显示,若没有则无变化。
AlarmManger常用方法有三个:
set(int type,long startTime,PendingIntent pi);//一次性
setExact(int type, long triggerAtMillis, PendingIntent operation)//一次性的精确版
setRepeating(int type,long startTime,long intervalTime,PendingIntentpi);//精确重复
setInexactRepeating(int type,long startTime,longintervalTime,PendingIntent pi);//非精确,降低功耗
各个参数含义:type表示闹钟类型,startTime表示闹钟第一次执行时间,long intervalTime表示间隔时间,PendingIntent表示闹钟响应动作。
type闹钟类型分为:
AlarmManager.ELAPSED_REALTIME:休眠后停止,相对开机时间
AlarmManager.ELAPSED_REALTIME_WAKEUP:休眠状态仍可唤醒cpu继续工作,相对开机时间
AlarmManager.RTC:同1,但时间相对于绝对时间
AlarmManager.RTC_WAKEUP:同2,但时间相对于绝对时间
AlarmManager.POWER_OFF_WAKEUP:关机后依旧可用,相对于绝对时间
startTime:闹钟的第一次执行时间,以毫秒为单位,一般使用当前时间。
intervalTime:执行时间间隔。
PendingIntent :PendingIntent用于描述Intent及其最终的行为.,这里用于获取定时任务的执行动作。
常见使用方式:利用AlarmManger+Service+BarocastReceiver来实现可唤醒cpu,甚至实现精确定时,适用于配合service在后台执行一些长期的定时行为。
实现倒计时的方法
倒计时几种方法呢,这个不像正计时那样简单实现容易想到的那5种,甚至正计时通过总量减的方式全可以转化成倒计时。所以在此我想介绍另一个专门倒计时的类CountDownTimer来讲解。
构造方法:
CountDownTimer (long millisInFuture, long countDownInterval)
//millisInFuture:设置倒计时的总时间(毫秒)
//countDownInterval:设置每次减去多少毫秒
验证码获取的示例代码如下:
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final int LEFT_TIME = 999;
private static final int TIME_OVER = 998;
private Button bt_verify;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bt_verify = (Button) findViewById(R.id.bt_verify);
bt_verify.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
setTimer();
}
});
}
private void setTimer(){
timer.start();
}
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case LEFT_TIME:
long left_time = (long) msg.obj;
bt_verify.setEnabled(false);//禁止button点击
bt_verify.setText((left_time / 1000) + " S");
break;
case TIME_OVER:
bt_verify.setEnabled(true);
bt_verify.setText("点击重发");
break;
default:
break;
}
}
};
private void stopTimer(){
timer.cancel();
}
@Override
protected void onDestroy() {
super.onDestroy();
stopTimer();
}
long millisInFuture = 60*1000;
long countDownInterval =1000;
CountDownTimer timer = new CountDownTimer(millisInFuture,countDownInterval) {
@Override
public void onTick(long millisUntilFinished) {
//millisUntilFinished 剩余时间回调,这个是实时的(以countDownInterval为单位)
Message msg = Message.obtain();
msg.what = LEFT_TIME;
msg.obj = millisUntilFinished;
mHandler.sendMessage(msg);
}
@Override
public void onFinish() {
//结束时的回调
mHandler.sendEmptyMessage(TIME_OVER);
}
};
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/bt_verify"
android:layout_marginRight="5dp"/>
<Button
android:id="@+id/bt_verify"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="获取验证码"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"/>
</RelativeLayout>
</RelativeLayout>
需要注意的是:CountDownTimer如果使用不当,常常会报空指针异常,甚至造成严重的内存泄漏,不过解决办法也是有的:
一是在CountDownTimer的onTick方法中记得判空:
activity中:
if(!activity.isFinishing()){
//doing something...
}
fragment中:
if(getActivity()!=null){
//doing something...
}
二还需要在宿主Activity或fragment生命周期结束的时候,记得调用timer.cancle()方法:
@Override
public void onDestroy() {
if(timer!=null){
timer.cancel();
timer = null;
}
super.onDestroy();
}
总结:
短期的定时任务推荐用最后两种方式方式实现,长期或者有精确要求的定时任务则选择AlarmManger + Service在后台执行。最后把开始的展示图去壳之后内部实现了一遍,内容比较简单,整篇文中最值得注意的OOM问题是开发中常常忽视的,减少OOM和ANR的情况,才可以将代码的细节把控好!
如果本文对你有所帮助,请点赞!你的鼓励是我写作的最大动力!
欢迎关注冯裴添的简书!
关注我的技术公众号,我会不定期推送关于安卓的文章,内容包含Android日常开发遇到中的问题、常用框架的介绍以及需要熟记的概念等等。
微信扫一扫下方的二维码即可关注: