发送短信验证码后,一般在界面上都会有一个倒计时的显示.在安卓中,实现类似的倒计时有多种方式,当然背后的基本原理都是设定一个初始值,然后每过一定的间隔时间执行操作.
1.用安卓自带的CountDownTimer实现
这是最简洁的实现方式.安卓提供了一个CountDownTimer类用于倒计时功能.其使用方法在注释里面写的已经比较清楚了.记时开启后禁掉控件的点击事件,倒计时结束后再开启.防止重复点击导致多个任务运行.
private void countDownTime() {
//用安卓自带的CountDownTimer实现
CountDownTimer mTimer = new CountDownTimer(60 * 1000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
Log.i(TAG, "millisUntilFinished: " + millisUntilFinished);
btn1.setText(millisUntilFinished / 1000 + "秒后重发");
}
@Override
public void onFinish() {
btn1.setEnabled(true);
btn1.setText("发送验证码");
cancel();
}
};
mTimer.start();
btn1.setEnabled(false);
}
CountDownTimer内部也是用Handler实现的.形参是总记时时间和时间间隔.当倒计时任务开始后,在handleMessage方法中会不断调用sendMessageDelayed方法,相当于每隔一段时间后回调onTick方法,这里就可以做界面实时更新的逻辑.内部有一个millisLeft记录剩余的时间,<=0时会回调onFinish方法.此时最好调用一次cancel方法,内部的Handler会移除之前定义的消息.
但是发送通知的间隔时间并非直接等于传入的参数,而是会通过SystemClock.elapsedRealtime()方法计算得出.这个值得到的是手机本次开机后到当前的总时间.每次累计的时候会有几毫秒的误差,最终导致显示的时候某个时间值可能不是很准确.上面的日志显示了倒计时开始后的实时时间,预先定义的间隔为1000毫秒.
2.用Java的TimerTask配合Timer实现(定时任务)
private void usingTimer() {
//使用Java的Timer配合TimerTask(定时任务)
time = 60;
final Timer timer = new Timer();
mTimerTask=new TimerTask() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (time <= 0) {
cancel();
btn2.setEnabled(true);
btn2.setText("发送验证码");
} else {
btn2.setEnabled(false);
btn2.setText(time + "秒后重发");
}
Log.i(TAG, "time: " + time);
time--;
}
});
}
};
timer.schedule(mTimerTask, 0, 1000);
}
该方法的原理是开启了一个线程,并执行定时操作.因为TimerTask是Java中一个实现了Runnable接口的抽象类,配合Timer类可以完成一段时间或者重复性的任务.Timer类中的schedule方法将二者联系在一起,简单的说Timer负责记时,TimerTask负责执行具体的任务.这里我们定义了一个倒计时的初始值time=60(s),run方法每隔给定的时间间隔后执行一次.当倒计时结束后一定要手动调用cancel,否则后台任务会一直执行下去,造成资源的浪费.更新控件的操作要放在UI线程中操作.
这种方法的好处是时间值相对准确,并且应用场景不止于验证码倒计时.将定义的time类型修改为static,还可以实现倒计时在页面销毁后正常计时,再次创建页面后仍显示准确的值.
3.用Handler的方式实现
private static class MyHandler extends Handler {
WeakReference<Main2Activity> mReference;
private MyHandler(Main2Activity activity) {
mReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
Main2Activity main2Activity = mReference.get();
if (main2Activity.time > 0) {
main2Activity.btn3.setText(main2Activity.time + "秒后重发");
main2Activity.btn3.setEnabled(false);
} else {
main2Activity.btn3.setText("发送验证码");
main2Activity.btn3.setEnabled(true);
}
}
}
基本原理就是利用Handler在主线程和子线程之间进行通信.此处的时间间隔用的是线程休眠1000毫秒,基本和延迟发送消息一个意思.需要注意的是内部类会持有外部类的引用,因此Activity销毁后Handler中有耗时操作还持有其引用,就会造成内存泄露.因此需要将Handler声明为静态内部类,并且持有Activity的弱引用,这样可以操作类中声明的控件和变量,而不必将它们全部声明为静态的.