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

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

用安卓自带的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: 59999
11-21 11:51:19.530 12704-12704/com.mercury.countdown I/Main2Activity: millisUntilFinished: 58998
11-21 11:51:20.532 12704-12704/com.mercury.countdown I/Main2Activity: millisUntilFinished: 57996
11-21 11:51:21.534 12704-12704/com.mercury.countdown I/Main2Activity: millisUntilFinished: 56993
11-21 11:51:22.538 12704-12704/com.mercury.countdown I/Main2Activity: millisUntilFinished: 55990
11-21 11:51:23.540 12704-12704/com.mercury.countdown I/Main2Activity: millisUntilFinished: 54988
11-21 11:51:24.542 12704-12704/com.mercury.countdown I/Main2Activity: millisUntilFinished: 53986
11-21 11:51:25.544 12704-12704/com.mercury.countdown I/Main2Activity: millisUntilFinished: 52984
11-21 11:51:26.545 12704-12704/com.mercury.countdown I/Main2Activity: millisUntilFinished: 51982
11-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的弱引用,这样可以操作类中声明的控件和变量,而不必将它们全部声明为静态的.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值