handler實現精確計時

首先說下關於handler自身的偏差:

若是使用handler.postDealyed(……, 1000)方式來進行每秒的計時,是不許確的,是的,有很大偏差,偏差的緣由在於在你收到消息,到你從新發出handler.postDealyed的時間,並非瞬間完成的,這裏面有不少邏輯處理的時間,即便沒有邏輯處理的時間,handler自己也是耗損性能的,因此消息並不可能按照理想的1000延遲來進行發送,這就致使了偏差的累積。java

代碼:

時鐘接口:android

public interface IDigitalClock {
    /**
 * 開始計時
 */
 void start();
 /**
 * 中止
 */
 void stop();
 /**
 * 時鐘復位
 */
 void reset();
 /**
 * 重啓
 */
 void restart();
}

正計時時鐘:git

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
import java.text.DecimalFormat;

public class DefaultDigitalClock implements IDigitalClock {
    private final static String TAG = "DefaultDigitalClock";
    private final static int TICK_EVENT = 0x1001;
    private Ticker mTicker;
    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            if (msg.what == TICK_EVENT) {
                long seconds = (long) msg.obj;
                String HHMMss = formatElapsedTime(seconds);
                clock.tick(seconds, HHMMss);
            }
            return false;
        }
    });
    private long startTime;
    private long elapsedSeconds;
    private long maxSeconds;
    private ClockOnMainThread clock;
    public DefaultDigitalClock(ClockOnMainThread clock) {
        this(-1, clock);
    }
    public DefaultDigitalClock(long maxSeconds, ClockOnMainThread clock) {
        this.maxSeconds = maxSeconds;
        this.clock = clock;
        this.elapsedSeconds = -1;
        this.startTime = -1;
    }
    @Override
    public void start() {
        startTime = System.currentTimeMillis();
        mTicker = new Ticker();
        long now = SystemClock.uptimeMillis();
        long next = now + (1000 - now % 1000);
        handler.postAtTime(mTicker, next);
    }
    @Override
    public void stop() {
        handler.removeMessages(TICK_EVENT);
        if (mTicker != null) {
            handler.removeCallbacks(mTicker);
        }
    }
    @Override
    public void reset() {
        elapsedSeconds = -1;
        startTime = -1;
        handler.sendMessage(newTick(0));
    }
    @Override
    public void restart() {
        stop();
        reset();
        start();
    }
    /**
     * 在每秒的整點執行
     * {@link "https://blog.csdn.net/cpcpcp123/article/details/88542113"}
     */ 
     private final class Ticker implements Runnable {
        public void run() {
            onTimeChanged();
            // 在設定秒數後結束
            if (maxSeconds > 0 && elapsedSeconds == maxSeconds) {
                stop();
                return;
            }
            long now = SystemClock.uptimeMillis();
            long next = now + (1000 - now % 1000);
            handler.postAtTime(this, next);
        }
    };
    /**
     * 計算時間變化
     */
    private void onTimeChanged() {
        if (Thread.currentThread() != Looper.getMainLooper().getThread()) {
            Log.e(TAG, "onTimeChanged() must work on main thread!");
            return;
        }
        elapsedSeconds = (System.currentTimeMillis() - startTime) / 1000;
        Log.d(TAG, String.valueOf(elapsedSeconds));
        String HHMMss = formatElapsedTime(elapsedSeconds);
        clock.tick(elapsedSeconds, HHMMss);
    }
    private Message newTick(long seconds) {
        Message msg = new Message();
        msg.what = TICK_EVENT;
        msg.obj = seconds;
        return msg;
    }
    /**
     * @see android.text.format.DateUtils#formatElapsedTime(long)
     * @param elapsedSeconds 通過的秒數
     */
    private String formatElapsedTime(long elapsedSeconds) {
        // Break the elapsed seconds into hours, minutes, and seconds.
        long hours = 0;
        long minutes = 0;
        long seconds = 0;
        if (elapsedSeconds >= 3600) {
            hours = elapsedSeconds / 3600;
            elapsedSeconds -= hours * 3600;
        }
        if (elapsedSeconds >= 60) {
            minutes = elapsedSeconds / 60;
            elapsedSeconds -= minutes * 60;
        }
        seconds = elapsedSeconds;
        String hh = new DecimalFormat("00").format(hours);
        String mm = new DecimalFormat("00").format(minutes);
        String ss = new DecimalFormat("00").format(seconds);
        return String.format("%s:%s:%s", hh, mm, ss);
    }
    
    public interface ClockOnMainThread {
        void tick(long seconds, String time);
    }
}

參考博客

相關文章
相關標籤/搜索