安卓中短信驗證碼倒計時的幾種方式

發送短信驗證碼後,一般在界面上都會有一個倒計時的顯示.在安卓中,實現類似的倒計時有多種方式,當然背後的基本原理都是設定一個初始值,然後每過一定的間隔時間執行操作.

用安卓自帶的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的弱引用,這樣可以操作類中聲明的控件和變量,而不必將它們全部聲明爲靜態的.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章