安卓中短信验证码倒计时的几种方式

来源:互联网 发布:mac如何输入emoji 编辑:程序博客网 时间:2024/06/02 06:10

发送短信验证码后,一般在界面上都会有一个倒计时的显示.在安卓中,实现类似的倒计时有多种方式,当然背后的基本原理都是设定一个初始值,然后每过一定的间隔时间执行操作.

用安卓自带的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会移除之前定义的消息.

11-21 11:51:18.529 12704-12704/com.mercury.countdown I/Main2Activity: millisUntilFinished: 5999911-21 11:51:19.530 12704-12704/com.mercury.countdown I/Main2Activity: millisUntilFinished: 5899811-21 11:51:20.532 12704-12704/com.mercury.countdown I/Main2Activity: millisUntilFinished: 5799611-21 11:51:21.534 12704-12704/com.mercury.countdown I/Main2Activity: millisUntilFinished: 5699311-21 11:51:22.538 12704-12704/com.mercury.countdown I/Main2Activity: millisUntilFinished: 5599011-21 11:51:23.540 12704-12704/com.mercury.countdown I/Main2Activity: millisUntilFinished: 5498811-21 11:51:24.542 12704-12704/com.mercury.countdown I/Main2Activity: millisUntilFinished: 5398611-21 11:51:25.544 12704-12704/com.mercury.countdown I/Main2Activity: millisUntilFinished: 5298411-21 11:51:26.545 12704-12704/com.mercury.countdown I/Main2Activity: millisUntilFinished: 5198211-21 11:51:27.548 12704-12704/com.mercury.countdown I/Main2Activity: millisUntilFinished: 50980

但是发送通知的间隔时间并非直接等于传入的参数,而是会通过SystemClock.elapsedRealtime()方法计算得出.这个值得到的是手机本次开机后到当前的总时间.每次累计的时候会有几毫秒的误差,最终导致显示的时候某个时间值可能不是很准确.上面的日志显示了倒计时开始后的实时时间,预先定义的间隔为1000毫秒.

用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,还可以实现倒计时在页面销毁后正常计时,再次创建页面后仍显示准确的值.

用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);        }    }}
//触发倒计时的点击事件private void useHandler() {    final MyHandler handler = new MyHandler(this);    time = 60;    Thread thread=new Thread(new Runnable() {        @Override        public void run() {            while (time >= 0) {                time--;                handler.sendEmptyMessage(0);                try {                    Thread.sleep(1000);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }    });    thread.start();}

基本原理就是利用Handler在主线程和子线程之间进行通信.此处的时间间隔用的是线程休眠1000毫秒,基本和延迟发送消息一个意思.需要注意的是内部类会持有外部类的引用,因此Activity销毁后Handler中有耗时操作还持有其引用,就会造成内存泄露.因此需要将Handler声明为静态内部类,并且持有Activity的弱引用,这样可以操作类中声明的控件和变量,而不必将它们全部声明为静态的.