如何测量经过的时间

49
我有一个包含10和20个问题的游戏。我需要计算用户完成游戏所用的时间。
Timer T=new Timer();
T.scheduleAtFixedRate(new TimerTask() {         
    @Override
    public void run() {
        runOnUiThread(new Runnable()
        {                
            public void run()
            {
                countdown.setText(""+count);
                count++;                
            }
        });
    }
}, 1000, 1000);

我使用这个来停止计数器:
T.cancel();

现在我需要两件事情:
  • 一种计算经过时间并将其存储在变量中的方法
  • 我需要最终值是一个双精度浮点数,例如最终得分为:15.49秒。

@Benjamin有一个更好的答案。 - necromancer
可能是如何在Java中计算事件的经过时间?的重复问题。 - Steve Chambers
这不是重复的问题,因为那个问题涉及到Java的一般情况,而这个问题则是针对Android特定的。两者之间的最佳实践解决方案是不同的。 - Jade
5个回答

88
当游戏开始时:
long tStart = System.currentTimeMillis();

当游戏结束时:

long tEnd = System.currentTimeMillis();
long tDelta = tEnd - tStart;
double elapsedSeconds = tDelta / 1000.0;

30
根据 文档System.currentTimeMillis() 是标准的“墙上”时钟(时间和日期),表示自纪元以来的毫秒数。墙上时钟可以由用户或电话网络设置(参见 setCurrentTimeMillis(long)),因此时间可能会不可预测地向前或向后跳动。因此,使用 System.currentTimeMillis() 实现经过时间是一个不好的选择。 - Jade
7
@Benjamin 你有什么建议? - poitroae
22
根据Android文档(http://developer.android.com/reference/android/os/SystemClock.html),SystemClock.elapsedRealtime()是“推荐用于一般目的间隔计时的基础”。 - Jade
正如其他人所说,下面的elapsedRealtime()答案是正确的。这个是错误的。 - HughHughTeotl

70
根据Android docsSystemClock.elapsedRealtime()是推荐用于一般目的的间隔计时的基础。这是因为,根据文档,elapsedRealtime() 保证单调递增,[...],因此是推荐用于一般目的的间隔计时的基础。 documentation中的SystemClock文档概述了各种时间方法及其适用的用例。
  • SystemClock.elapsedRealtime()SystemClock.elapsedRealtimeNanos()是计算通用经过时间的最佳选择。
  • SystemClock.uptimeMillis()System.nanoTime()也可以使用,但与建议的方法不同,它们不包括深度睡眠中的时间。如果这是您想要的行为,则可以使用它们。否则,请坚持使用elapsedRealtime()
  • 避免使用System.currentTimeMillis(),因为它将返回“挂钟”时间。这对于计算经过时间是不适合的,因为挂钟时间可能向前或向后跳动。许多事情,比如NTP客户端,都会导致挂钟时间跳动和偏移。这将导致基于currentTimeMillis()的经过时间计算并不总是准确。

游戏开始时:

long startTime = SystemClock.elapsedRealtime();

当游戏结束时:
long endTime = SystemClock.elapsedRealtime();
long elapsedMilliSeconds = endTime - startTime;
double elapsedSeconds = elapsedMilliSeconds / 1000.0;

此外,Timer()是一种尽力而为的计时器,并不总是准确的。因此,在游戏持续时间内会有时间误差的积累。为了更准确地显示中间时间,请使用定期检查System.currentTimeMillis()作为发送到setText(...)的时间基础。

此外,您可能希望使用TimerTask而不是使用Timer,这个类专门为您想要做的事情而设计。唯一的问题是它是倒计时而不是正计时,但可以通过简单的减法来解决。


1
这是正确的答案。被接受的那个实际上是错误的。 - HughHughTeotl

13

更好的是!

long tStart = System.nanoTime();
long tEnd = System.nanoTime();
long tRes = tEnd - tStart; // time in nanoseconds

阅读关于nanoTime()的文档


1
如果你提到了一些新的东西,提供文档链接是很好的。 - Krzysztof Safjanowski
根据这个问题的回答,这个答案可能是最好的。 - Clément

2
从Java 8开始,您可以尝试以下操作:

import java.time.*;
import java.time.temporal.ChronoUnit;

Instant start_time = Instant.now();
// Your code
Instant stop_time = Instant.now();

System.out.println(Duration.between(start_time, stop_time).toMillis());

//or

System.out.println(ChronoUnit.MILLIS.between(start_time, stop_time));

1
有许多方法可以实现这一点,但衡量经过时间的最重要考虑因素是使用System.nanoTime()TimeUnit.NANOSECONDS作为时间单位。为什么要这样做呢?嗯,因为System.nanoTime()方法返回一个高分辨率时间源,以纳秒为单位自某个参考点(即Java虚拟机的启动)起计算。

此方法只能用于测量经过时间,并与任何其他系统或挂钟时间概念无关。

出于同样的原因,建议避免使用System.currentTimeMillis()方法来测量经过时间。该方法返回挂钟时间,可能会根据许多因素而改变。这将对你的测量结果产生负面影响。

请注意,虽然返回值的时间单位是毫秒,但值的粒度取决于底层操作系统并且可能更大。例如,许多操作系统按十毫秒为单位测量时间。

所以这里有一个基于System.nanoTime()方法的解决方案,另一个使用Guava,最后一个使用Apache Commons Lang

public class TimeBenchUtil
{
    public static void main(String[] args) throws InterruptedException
    {
        stopWatch();
        stopWatchGuava();
        stopWatchApacheCommons();
    }

    public static void stopWatch() throws InterruptedException
    {
        long endTime, timeElapsed, startTime = System.nanoTime();

        /* ... the code being measured starts ... */

        // sleep for 5 seconds
        TimeUnit.SECONDS.sleep(5);

        /* ... the code being measured ends ... */

        endTime = System.nanoTime();

        // get difference of two nanoTime values
        timeElapsed = endTime - startTime;

        System.out.println("Execution time in nanoseconds   : " + timeElapsed);
    }

    public static void stopWatchGuava() throws InterruptedException
    {
        // Creates and starts a new stopwatch
        Stopwatch stopwatch = Stopwatch.createStarted();

        /* ... the code being measured starts ... */

        // sleep for 5 seconds
        TimeUnit.SECONDS.sleep(5);
        /* ... the code being measured ends ... */

        stopwatch.stop(); // optional

        // get elapsed time, expressed in milliseconds
        long timeElapsed = stopwatch.elapsed(TimeUnit.NANOSECONDS);

        System.out.println("Execution time in nanoseconds   : " + timeElapsed);
    }

    public static void stopWatchApacheCommons() throws InterruptedException
    {
        StopWatch stopwatch = new StopWatch();
        stopwatch.start();

        /* ... the code being measured starts ... */

        // sleep for 5 seconds
        TimeUnit.SECONDS.sleep(5);

        /* ... the code being measured ends ... */

        stopwatch.stop();    // Optional

        long timeElapsed = stopwatch.getNanoTime();

        System.out.println("Execution time in nanoseconds   : " + timeElapsed);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接