【问题标题】:android countdowntimer tick is not accurateandroid countdowntimer刻度不准确
【发布时间】:2014-04-27 13:08:38
【问题描述】:

我正在使用倒数计时器进行音频通知...而且从一开始就不准确...

使用初始参数

private final long startCountDown; 
private final long intervalCountDown;
    ...
    startCountDown = 180 * 1000;   // 3 mns  - to be set from Preferences later
intervalCountDown = 60 * 1000;   // 1 mns - to be set from Preferences later
    ...
    public void onTick(long millisUntilFinished) {
       Log.d(TAG, "notify countDown: " + millisUntilFinished + " msecs");
    }


    countDownTimer = new SwimCountDownTimer(startCountDown,intervalCountDown);
    ....

public void startCountDown() {
    Log.d(TAG, "start countDown for " + startCountDown + " msecs" );
    countDownTimer.start();
}

我可以在日志中看到初始倒计时正确设置为 180000,但下一个应该是 120000 并且设置为 119945 !!!

04-27 14:50:42.146: I/SWIMMER(8670): notify countDown: 180000 msecs
04-27 14:51:42.206: I/SWIMMER(8670): notify countDown: 119945 msecs

这很烦人,因为音频通知器只希望说“2 分钟”而不是“1 分 59 秒”......;为什么间隔不正确......? 我可以在将文本设置为语音字符串时尝试它...但是有什么方法可以获取正确的数据吗?

感谢建议

【问题讨论】:

  • 55毫秒有这么大的问题吗?这是 1/20 秒的时间。大多数人无法辨别其中的区别。
  • 如果它一直慢约 55 毫秒,并且永远不会超过 120000 毫秒,那么这可能就是调用 onTick 方法所需的时间。把它四舍五入。
  • 谢谢...问题不在于准确性,而是重用音频通知的值,所以我猜四舍五入是 Greg 提到的解决方案... 120000ms 是“2 分钟”音频消息... 119945不是...

标签: java android countdowntimer


【解决方案1】:

我知道这是一个老问题——但我也遇到过这个问题,我想我会分享我的解决方案。

显然 CountDownTimer 不是很准确,所以我决定使用 java.util.Timer 实现更精确的倒数计时器:

public abstract class PreciseCountdown extends Timer {
    private long totalTime, interval, delay;
    private TimerTask task;
    private long startTime = -1;
    private boolean restart = false, wasCancelled = false, wasStarted = false;

    public PreciseCountdown(long totalTime, long interval) {
        this(totalTime, interval, 0);
    }

    public PreciseCountdown(long totalTime, long interval, long delay) {
        super("PreciseCountdown", true);
        this.delay = delay;
        this.interval = interval;
        this.totalTime = totalTime;
        this.task = getTask(totalTime);
    }

    public void start() {
        wasStarted = true;
        this.scheduleAtFixedRate(task, delay, interval);
    }

    public void restart() {
        if(!wasStarted) {
            start();
        }
        else if(wasCancelled) {
            wasCancelled = false;
            this.task = getTask(totalTime);
            start();
        }
        else{
            this.restart = true;
        }
    }

    public void stop() {
        this.wasCancelled = true;
        this.task.cancel();
    }

    // Call this when there's no further use for this timer
    public void dispose(){
        cancel();
        purge();
    }

    private TimerTask getTask(final long totalTime) {
        return new TimerTask() {

            @Override
            public void run() {
                long timeLeft;
                if (startTime < 0 || restart) {
                    startTime = scheduledExecutionTime();
                    timeLeft = totalTime;
                    restart = false;
                } else {
                    timeLeft = totalTime - (scheduledExecutionTime() - startTime);

                    if (timeLeft <= 0) {
                        this.cancel();
                        startTime = -1;
                        onFinished();
                        return;
                    }
                }

                onTick(timeLeft);
            }
        };
    }

    public abstract void onTick(long timeLeft);
    public abstract void onFinished();
}

用法示例如下:

this.countDown = new PreciseCountdown(totalTime, interval, delay) {
    @Override
    public void onTick(long timeLeft) {
        // update..
        // note that this runs on a different thread, so to update any GUI components you need to use Activity.runOnUiThread()
    }

    @Override
    public void onFinished() {
        onTick(0); // when the timer finishes onTick isn't called
        // count down is finished
    }
};

要开始倒计时,只需调用 countDown.start()。 countDown.stop() 停止 countDown,可以使用 countDown.restart() 重新启动。

希望这对将来的任何人都有帮助。

【讨论】:

  • 伙计,你太棒了)你可能是我在做测试作业时得到新工作的原因)))
  • 非常感谢!你救了我的命!
【解决方案2】:

除了使用millisUntilFinished,您可以使用一个变量来保存剩余时间,并在每个onTick 中减去带有间隔的变量。这样remainingTime总是准确的。

private class MyTimer(
    countDownTime: Long, 
    interval: Long
) : CountDownTimer(countDownTime, interval) {

    private var remainingTime = countDownTime

    override fun onFinish() {
    }

    override fun onTick(millisUntilFinished: Long) {
        // consume remainingTime here and then minus interval
        remainingTime -= interval
    }
}

【讨论】:

    【解决方案3】:

    这是对 Noam Gal 发布的内容的扩展。我添加了额外的功能,您可以在其中暂停和恢复计时器。这对我来说很有帮助。

    public abstract class PreciseCountdownTimer extends Timer {
    
        private long totalTime, interval, delay;
        private TimerTask task;
        private long startTime = -1;
        private long timeLeft;
        private boolean restart = false;
        private boolean wasCancelled = false;
        private boolean wasStarted = false;
    
        public PreciseCountdownTimer(long totalTime, long interval) {
            this(totalTime, interval, 0);
        }
    
    
        public PreciseCountdownTimer(long totalTime, long interval, long delay ) {
            super("PreciseCountdownTimer", true);
            this.delay = delay;
            this.interval = interval;
            this.totalTime = totalTime;
            this.task = buildTask(totalTime);
        }
    
        private TimerTask buildTask(final long totalTime) {
            return new TimerTask() {
    
                @Override
                public void run() {
                    if (startTime < 0 || restart) {
                        startTime = scheduledExecutionTime();
                        timeLeft = totalTime;
                        restart = false;
                    } else {
                        timeLeft = totalTime - (scheduledExecutionTime() - startTime);
    
                        if (timeLeft <= 0) {
                            this.cancel();
                            wasCancelled = true;
                            startTime = -1;
                            onFinished();
                            return;
                        }
                    }
    
                    onTick(timeLeft);
                }
            };
        }
    
        public void start() {
            wasStarted = true;
            this.scheduleAtFixedRate(task, delay, interval);
        }
    
        public void stop() {
            this.wasCancelled = true;
            this.task.cancel();
        }
    
        public void restart() {
            if (!wasStarted) {
                start();
            } else if (wasCancelled) {
                wasCancelled = false;
                this.task = buildTask(totalTime);
                start();
            } else {
                this.restart = true;
            }
        }
    
        public void pause(){
            wasCancelled = true;
            this.task.cancel();
            onPaused();
        }
    
        public void resume(){
            wasCancelled = false;
            this.task = buildTask(timeLeft);
            this.startTime = - 1;
            start();
            onResumed();
        }
    
        // Call this when there's no further use for this timer
        public void dispose() {
            this.cancel();
            this.purge();
        }
    
        public abstract void onTick(long timeLeft);
    
        public abstract void onFinished();
    
        public abstract void onPaused();
    
        public abstract void onResumed();
    }
    

    用法示例几乎完全相同:

    this.timer = new PreciseCountdownTimer(totalTime, interval, delay) {
                @Override
                public void onTick(long timeLeft) {
                    // note that this runs on a different thread, so to update any GUI components you need to use Activity.runOnUiThread()
                }
    
                @Override
                public void onFinished() {
                    onTick(0); // when the timer finishes onTick isn't called
                    // count down is finished
                }
    
                @Override
                public void onPaused() {
                    // runs after the timer has been paused
                }
    
                @Override
                public void onResumed() {
                    // runs after the timer has been resumed
                }
            };
    

    尽情享受吧:D

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-16
      • 1970-01-01
      • 2021-11-03
      • 2015-03-27
      • 1970-01-01
      • 2010-09-05
      • 2013-03-13
      • 2015-02-05
      相关资源
      最近更新 更多