Android CountDownTimer 计时不准确问题

问题

使用CountDownTimer计时并不精准, 比如定时5分钟, 结果4:50多几秒钟就停止计时, 解决方式参考了Google TextClock控件

private final Runnable mTicker = new Runnable() {
    public void run() {
        if (mStopTicking) {
            return; // Test disabled the clock ticks
        }
        onTimeChanged();

        long now = SystemClock.uptimeMillis();
        long next = now + (1000 - now % 1000);

        Handler handler = getHandler();
        if (handler != null) {
            handler.postAtTime(mTicker, next);
        }
    }
};

解决方式

无需重写CountDownTimer

private val mTvDownTimer: TextView by lazy { findViewById(R.id.tv_countdown_timer) }
private val mCount: Long = 10
private val mCountDownTimer: CountDownTimer = object : CountDownTimer(mCount * 1000, 1000) {
    override fun onTick(millisUntilFinished: Long) {
        val next: Long = millisUntilFinished + (1000 - millisUntilFinished % 1000)
        val showTime: Long = Math.min(mCount, next / 1000)
        mTvDownTimer.text = "CountDownTimer 👉 $showTime"
        Log.w("123", "onTick millisUntilFinished=$millisUntilFinished  next=$next  showTime=$showTime")
    }
    override fun onFinish() {
        mTvDownTimer.text = "CountDownTimer 👉 0"
        Log.e("123", "onFinish")
    }
}

🍎 Log 日志 (注意第一行next=11000是超过了mCount的最大值,所以需要Math.min函数取小)

W/123: onTick millisUntilFinished=10000  next=11000  showTime=10
W/123: onTick millisUntilFinished=8998  next=9000  showTime=9
W/123: onTick millisUntilFinished=7996  next=8000  showTime=8
W/123: onTick millisUntilFinished=6993  next=7000  showTime=7
W/123: onTick millisUntilFinished=5991  next=6000  showTime=6
W/123: onTick millisUntilFinished=4989  next=5000  showTime=5
W/123: onTick millisUntilFinished=3987  next=4000  showTime=4
W/123: onTick millisUntilFinished=2985  next=3000  showTime=3
W/123: onTick millisUntilFinished=1984  next=2000  showTime=2
W/123: onTick millisUntilFinished=983  next=1000  showTime=1
E/123: onFinish

🍌 再加上暂停/继续功能 (GitHub 👉 https://github.com/javakam/AndoTemplate/blob/master/ando_toolkit/src/main/java/ando/toolkit/FixedCountDownTimer.java )

完整代码:

/**
 * 基于 API 30 的 CountDownTimer 做了以下调整:
 * <p>
 * 1. 修复计时不准的问题;
 * 2. 增加 暂停/继续
 * </p>
 *
 * @author javakam
 */
public class FixedCountDownTimer {

    public interface Listener {
        void onTick(long fixedMillisUntilFinished);

        void onFinish();
    }

    private static final long SECOND_MILLIS = 1000L;

    /**
     * Millis since epoch when alarm should stop.
     */
    private final long mMillisInFuture;

    /**
     * The interval in millis that the user receives callbacks
     */
    private final long mCountdownInterval;

    private long mStopTimeInFuture;

    /**
     * 剩余时间(单位毫秒)
     */
    private long mMillisUntilFinished;

    /**
     * boolean representing if the timer was cancelled
     */
    private boolean mCancelled = false;

    /**
     * 暂停的状态
     */
    private boolean mPaused = false;

    /**
     * 是否正在倒计时
     */
    private boolean mIsRunning = false;

    private Listener mListener;

    public FixedCountDownTimer(long millisInFuture, long countDownInterval) {
        //注: 由于 sendMessageDelayed 的机制造成的 1000 毫秒的延时
        mMillisInFuture = millisInFuture;
        mCountdownInterval = countDownInterval;
    }

    public void setListener(Listener l) {
        this.mListener = l;
    }

    /**
     * Start the countdown.
     */
    public synchronized final FixedCountDownTimer start() {
        mCancelled = false;
        if (mMillisInFuture <= 0) {
            onFinish();
            return this;
        }
        mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
        mHandler.sendMessage(mHandler.obtainMessage(MSG));
        mIsRunning = true;
        mPaused = false;
        return this;
    }

    public synchronized final void pause() {
        mMillisUntilFinished = mStopTimeInFuture - SystemClock.elapsedRealtime();
        mIsRunning = false;
        mPaused = true;
    }

    public synchronized long resume() {
        // 结束的时间设置为当前时间加剩余时间
        mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisUntilFinished;
        mHandler.sendMessage(mHandler.obtainMessage(MSG));
        mIsRunning = true;
        mPaused = false;
        return mMillisUntilFinished;
    }

    /**
     * Cancel the countdown.
     */
    public synchronized final void cancel() {
        mHandler.removeMessages(MSG);
        mCancelled = true;
        mIsRunning = false;
        mPaused = false;
    }

    /**
     * Callback fired on regular interval.
     *
     * @param millisUntilFinished The amount of time until finished.
     */
    private void onTick(long millisUntilFinished) {
        if (mListener != null) {
            //修复时间不准的问题, 参考 TextClock
            final long fixedMillisUntilFinished = millisUntilFinished + (SECOND_MILLIS - millisUntilFinished % SECOND_MILLIS);
            /*
            millisUntilFinished=1998; fixedMillisUntilFinished=2000
            millisUntilFinished=997; fixedMillisUntilFinished=1000
            ...
             */
            Log.w("123", "millisUntilFinished=" + millisUntilFinished + "; fixedMillisUntilFinished=" + fixedMillisUntilFinished);
            mListener.onTick(fixedMillisUntilFinished);
        }
    }

    private void onFinish() {
        mIsRunning = false;
        if (mListener != null) {
            mListener.onFinish();
        }
    }

    public boolean isPaused() {
        return mPaused;
    }

    public boolean isRunning() {
        return mIsRunning;
    }

    private static final int MSG = 1;

    // handles counting down
    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            synchronized (FixedCountDownTimer.this) {
                if (mCancelled) {
                    return;
                }
                if (mPaused) {
                    removeMessages(MSG);
                    return;
                }

                final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();

                if (millisLeft <= 0) {
                    onFinish();
                } else {
                    long lastTickStart = SystemClock.elapsedRealtime();
                    onTick(millisLeft);

                    // take into account user's onTick taking time to execute
                    long lastTickDuration = SystemClock.elapsedRealtime() - lastTickStart;
                    long delay;

                    if (millisLeft < mCountdownInterval) {
                        // just delay until done
                        delay = millisLeft - lastTickDuration;

                        // special case: user's onTick took more than interval to
                        // complete, trigger onFinish without delay
                        if (delay < 0) {
                            delay = 0;
                        }
                    } else {
                        delay = mCountdownInterval - lastTickDuration;

                        // special case: user's onTick took more than interval to
                        // complete, skip to next interval
                        while (delay < 0) {
                            delay += mCountdownInterval;
                        }
                    }

                    sendMessageDelayed(obtainMessage(MSG), delay);
                }
            }
        }
    };

}

使用时候注意先判断是否处于运行中

if (mFixedCountDownTimer!=null&&!mFixedCountDownTimer.isRunning()) {
    if (mFixedCountDownTimer.isPaused()) {
        mFixedCountDownTimer.resume()
    } else {
        mFixedCountDownTimer.start()
    }
}

参考:

blog.csdn.net/u013719138/…

blog.csdn.net/cpcpcp123/a…

本文作者:风之旅人

本文链接:https://www.cnblogs.com/jooy/p/17302010.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   javakam  阅读(0)  评论(0编辑  收藏  举报  
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起