执行定时任务的方法:
package com.hjqjl.whdemo.feature.feature0.ui.activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import com.blankj.utilcode.util.ToastUtils;
import com.hjqjl.whdemo.R;
import com.hjqjl.whdemo.base.BaseActivity;
import java.util.Timer;
import java.util.TimerTask;
public class TimerActivity extends BaseActivity {
public static void start(Context context) {
Intent starter = new Intent(context, TimerActivity.class);
context.startActivity(starter);
}
private Timer timer;
private TimerTask task;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_timer);
}
@Override
protected void onStart() {
super.onStart();
startTimer();
}
@Override
protected void onStop() {
super.onStop();
stopTimer();
}
/**
* 启动定时器
*/
public void startTimer() {
if (timer == null) {
timer = new Timer();
}
if (task != null) {
task.cancel();
}
task = new TimerTask() {
@Override
public void run() {
//执行定时任务
ToastUtils.showShort("执行定时任务Timer");
}
};
if (timer != null) {
timer.schedule(task, 3000, 60000);
}
}
/**
* 停止定时器 刷新上传数据
*/
private void stopTimer() {
if (timer != null) {
task.cancel();
timer.cancel();
timer.purge();
timer = null;
}
}
}
package com.hjqjl.whdemo.feature.feature0.ui.activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import com.blankj.utilcode.util.ToastUtils;
import com.hjqjl.whdemo.R;
import com.hjqjl.whdemo.base.BaseActivity;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class TimerActivity extends BaseActivity {
public static void start(Context context) {
Intent starter = new Intent(context, TimerActivity.class);
context.startActivity(starter);
}
private ScheduledExecutorService exec;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_timer);
}
@Override
protected void onStart() {
super.onStart();
initTimer();
}
@Override
protected void onStop() {
super.onStop();
endExecutorScan();
}
private boolean isTimerRun = true;
private void initTimer() {
endExecutorScan();
exec = Executors.newSingleThreadScheduledExecutor();//启动定时线程池
if (exec != null) {
exec.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
if (isTimerRun) {
//执行定时任务
ToastUtils.showShort("执行定时任务ScheduledExecutorService");
}
}
},
3, 60, TimeUnit.SECONDS);
}
}
private void endExecutorScan() {
if (exec != null) {
isTimerRun = false;
exec.shutdownNow();
}
exec = null;//非单例模式,置空防止重复的任务
}
}
实现如发送短信验证码等功能,通常需要倒计时功能,防止用户频繁按按钮发送短信。
效果如下:
import android.os.CountDownTimer;
import android.widget.Button;
/**
* 倒计时Button帮助类
* 用法
* CountDownButtonHelper countDownButtonHelper = new CountDownButtonHelper(smsBtn);
* countDownButtonHelper.start();//开始
* ----------------
* countDownButtonHelper.cancel();//取消
*/
public class CountDownButtonHelper {
// 倒计时timer
private CountDownTimer countDownTimer;
// 计时结束的回调接口
private OnFinishListener listener;
private Button mButton;
private String btnText;
/**
* @param button 需要显示倒计时的Button
*/
public CountDownButtonHelper(Button button) {
this.mButton = button;
// 由于CountDownTimer并不是准确计时,在onTick方法调用的时候,time会有1-10ms左右的误差,这会导致最后一秒不会调用onTick()
// 因此,设置间隔的时候,默认减去了10ms,从而减去误差。
// 经过以上的微调,最后一秒的显示时间会由于10ms延迟的积累,导致显示时间比1s长max*10ms的时间,其他时间的显示正常,总时间正常
countDownTimer = new CountDownTimer(10 * 1000, 1 * 1000 - 10) {//倒计时的最大值10s;倒计时的间隔1s
@Override
public void onTick(long time) {
// 第一次调用会有1-10ms的误差,因此需要+15ms,防止第一个数不显示,第二个数显示2s
mButton.setText("(" + ((time + 15) / 1000) + "秒)");
//Logger.d("time = " + (time) + " text = " + ((time + 15) / 1000));
}
@Override
public void onFinish() {
mButton.setEnabled(true);
if (btnText == null) {
mButton.setText("验证码");
} else {
mButton.setText(btnText);//设置button上的文字
}
if (listener != null) {
listener.finish();
}
}
};
}
/**
* 开始倒计时
*/
public void start() {
mButton.setEnabled(false);
countDownTimer.start();
}
/**
* 关闭倒计时,一般onDestroy里会用到
*/
public void cancel() {
if (countDownTimer != null) {
countDownTimer.cancel();
}
}
/**
* 设置倒计时结束的监听器
*
* @param listener
*/
public void setOnFinishListener(OnFinishListener listener) {
this.listener = listener;
}
/**
* 计时结束的回调接口(外部要调用所以用public)
*/
public interface OnFinishListener {
void finish();
}
/**
* 设置按钮里的文字
*/
public void setBtnText(String btnText) {
this.btnText = btnText;
}
}
在activity中的实现方法参考如下:
import android.content.Context;
import android.content.Intent;
import android.view.View;
import com.hjqjl.whdemo.databinding.ActivityCountDownBinding;
import com.hjqjl.whdemo.utils.CountDownButtonHelper;
import com.hjqjl.whdemo.utils.ToastUtils;
/**
* 倒计时按钮
*/
public class CountDownActivity extends BaseActivity {
public static void actionStart(Context context) {
Intent intent = new Intent(context, CountDownActivity.class);
context.startActivity(intent);
}
private CountDownButtonHelper countDownButtonHelper;
private ActivityCountDownBinding binding;
@Override
protected void initData() {
binding = ActivityCountDownBinding.inflate(getLayoutInflater());
View view = binding.getRoot();
setContentView(view);
countDownButtonHelper = new CountDownButtonHelper(binding.btnCountDown);
}
@Override
protected void processLogic() {
binding.btnCountDown.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
countDownButtonHelper.start();
}
});
countDownButtonHelper.setOnFinishListener(new CountDownButtonHelper.OnFinishListener() {
@Override
public void finish() {
ToastUtils.showToast(mContext, "倒计时结束调用");
}
});
}
@Override
protected void onDestroy() {
if (null != countDownButtonHelper) {
countDownButtonHelper.cancel();
}
super.onDestroy();
}
}
需要用到广播
package com.apep.fivefive.utils;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import com.apep.fivefive.receiver.ReLaunchAppReceiver;
import com.blankj.utilcode.util.LogUtils;
import com.blankj.utilcode.util.TimeUtils;
import com.blankj.utilcode.util.ToastUtils;
import java.util.Calendar;
import java.util.Random;
import java.util.TimeZone;
/**
* 2021/7/8 17:05 wh
*/
public class ReLaunchAppUtils {
public static void reLaunchApp(Context context, String msg) {
ToastUtils.make().setTextSize(50).setDurationIsLong(true).show("" + msg);
Intent intent = new Intent(context, ReLaunchAppReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
//得到AlarmManager实例
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC, System.currentTimeMillis() + (1000 * 10), pi);//定时10s之后
}
/**
* 凌晨一定时间内重启
*
* @param context 上下文
*/
public static void reLaunchApp2(Context context) {
//得到日历实例,主要是为了下面的获取时间
Calendar mCalendar = Calendar.getInstance();
mCalendar.setTimeInMillis(System.currentTimeMillis());
// 这里时区需要设置一下,不然可能个别手机会有8个小时的时间差
mCalendar.setTimeZone(TimeZone.getTimeZone("GMT+8"));
//是设置日历的时间,主要是让日历的年月日和当前同步
mCalendar.setTimeInMillis(System.currentTimeMillis());
//设置在几点提醒 设置的为点
int randomH = new Random().nextInt(4);
mCalendar.set(Calendar.HOUR_OF_DAY, randomH);
//设置在几分提醒 设置的为0分
int randomM = new Random().nextInt(60);
mCalendar.set(Calendar.MINUTE, randomM);
//下面这两个看字面意思也知道
int randomS = new Random().nextInt(60);
mCalendar.set(Calendar.SECOND, randomS);
//设置下一天
mCalendar.add(Calendar.DAY_OF_MONTH, 1);
// ToastUtils.make().setTextSize(50).setDurationIsLong(true).show("" + msg);
ReLaunchAppReceiver.class为广播接受者
Intent intent = new Intent(context, ReLaunchAppReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
//得到AlarmManager实例
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
LogUtils.v(TimeUtils.millis2String(mCalendar.getTimeInMillis()));
//**********注意!!下面的三个根据实际需求任选其一即可*********
/*
* 单次提醒
* RTC_WAKEUP 在设备关闭时唤醒设备。
* RTC 此警报不会唤醒设备;如果它在设备睡眠时关闭,则直到设备下次醒来时才会发送。
* mCalendar.getTimeInMillis() 上面设置的11点0分的时间点毫秒值
*/
am.set(AlarmManager.RTC_WAKEUP, mCalendar.getTimeInMillis(), pi);
/*
* 重复提醒
* 第一个参数是警报类型;下面有介绍
* 第二个参数网上说法不一,很多都是说的是延迟多少毫秒执行这个闹钟,但是我用的刷了MIUI的三星手机的实际效果是与单次提醒的参数一样,即设置的13点25分的时间点毫秒值
* 第三个参数是重复周期,也就是下次提醒的间隔 毫秒值 我这里是一天后提醒
*/
// am.setRepeating(AlarmManager.RTC_WAKEUP, mCalendar.getTimeInMillis(), (1000 * 60 * 60 * 24), pi);
/*
* 时间要求比set精准(耗能大)
*/
// am.setExact(AlarmManager.RTC_WAKEUP, mCalendar.getTimeInMillis(), pi);
}
/**
* 关闭定时
*/
private void stopRemind(Context context){
Intent intent = new Intent(context, ReLaunchAppReceiver .class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0,
intent, 0);
AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
//取消警报
am.cancel(pi);
Toast.makeText(this, "关闭了提醒", Toast.LENGTH_SHORT).show();
}
}
广播ReLaunchAppReceiver 不要忘记在AndroidManifest.xml中配置
public class ReLaunchAppReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//当系统到我们设定的时间点的时候会发送广播,执行这里
LogUtils.e("ReLaunchReceiver");
}
}