A

Android Vitals - 现在几点?现在几点? uptimeMillis() 与 nanoTime() 对比总结

2025-06-10

Android Vitals - 现在几点?

现在是几奌?

uptimeMillis()对比nanoTime()

结论

注:漂亮的标题照片来自 Romain Guy

昨天我有一个想法:

我收到了热情的回复,于是决定开始动笔。本系列博客将专注于生产环境中 Android 应用的稳定性和性能监控。它被命名为“Android Vitals”,因为它与 Google 自己的Android Vitals紧密相关:

Android Vitals 是 Google 为提升 Android 设备稳定性和性能而推出的一项计划。当选择加入的用户运行您的应用时,他们的 Android 设备会记录各种指标,包括应用稳定性、应用启动时间、电池使用情况、渲染时间和权限拒绝次数等数据。

如果您对未来的博客有任何问题或建议,请随时通过 Twitter联系我们

为了热身,我先从一个简单的问题开始:

现在是几奌?

为了跟踪性能,我们需要测量时间间隔,即两个时间点之间的差值。JDK 提供了两种获取当前时间的方法:

// Milliseconds since Unix epoch (00:00:00 UTC on 1 January 1970)
System.currentTimeMillis()
// Nanoseconds since the VM started.
System.nanoTime()

Android 提供了一个SystemClock类,其中添加了一些内容:

// (API 29) Clock that starts at Unix epoch.
// Synchronized using the device's location provider.
SystemClock.currentGnssTimeClock()
// Milliseconds running in the current thread.
SystemClock.currentThreadTimeMillis()
// Milliseconds since boot, including time spent in sleep.
SystemClock.elapsedRealtime()
// Nanoseconds since boot, including time spent in sleep.
SystemClock.elapsedRealtimeNanos()
// Milliseconds since boot, not counting time spent in deep sleep.
SystemClock.uptimeMillis()

我们应该选择哪一个?SystemClock javadoc可以帮助回答这个问题:

  • System#currentTimeMillis可由用户或手机网络设置,因此时间可能会不可预测地向前或向后跳动。间隔时间或已用时间的测量应使用不同的时钟
  • SystemClock#uptimeMillis在系统进入深度睡眠时停止。这是大多数间隔计时(例如 Thread#sleep(long)、Object#wait(long) 和 System#nanoTime)的基础。此时钟适用于间隔不跨越设备睡眠的间隔计时
  • SystemClock#elapsedRealtimeSystemClock#elapsedRealtimeNanos包含深度睡眠。此时钟是通用间隔计时的推荐基础

应用程序的性能对深度睡眠中发生的事情没有影响,所以我们最好的选择SystemClock.uptimeMillis()System.nanoTime()

uptimeMillis()对比nanoTime()

System.nanoTime()比 更精确uptimeMillis(),但这仅适用于微基准测试。在生产环境中跟踪性能时,我们需要毫秒级的分辨率。

让我们比较一下它们对性能的影响。我克隆了Android Benchmark Samples仓库,并添加了以下测试:

@LargeTest
@RunWith(AndroidJUnit4::class)
class TimingBenchmark {
    @get:Rule
    val benchmarkRule = BenchmarkRule()

    @Test
    fun nanoTime() {
        benchmarkRule.measureRepeated {
            System.nanoTime()
        }
    }

    @Test
    fun uptimeMillis() {
        benchmarkRule.measureRepeated {
            SystemClock.uptimeMillis()
        }
    }
}

在运行 Android 10 的 Pixel 3 上的结果:

  • System.nanoTime()中位时间:208 纳秒
  • SystemClock.uptimeMillis()中位时间:116 纳秒

SystemClock.uptimeMillis()速度几乎快了一倍!虽然这种差异应该不会对应用造成任何实际影响,但我们能找出它速度更快的原因吗?

uptimeMillis()执行

SystemClock.uptimeMillis()作为带有注释的本机方法实现@CriticalNative。CriticalNative为不包含对象的方法提供更快的 JNI 转换。

public final class SystemClock {
    @CriticalNative
    native public static long uptimeMillis();
}

来源

本机实现位于SystemClock.cpp

int64_t uptimeMillis()
{
    int64_t when = systemTime(SYSTEM_TIME_MONOTONIC);
    return (int64_t) nanoseconds_to_milliseconds(when);
}

来源

systemTime()定义于Timers.cpp

nsecs_t systemTime(int clock) {
    static constexpr clockid_t clocks[] = {
        CLOCK_REALTIME,
        CLOCK_MONOTONIC,
        CLOCK_PROCESS_CPUTIME_ID,
        CLOCK_THREAD_CPUTIME_ID,
        CLOCK_BOOTTIME
    };
    timespec t = {};
    clock_gettime(clocks[clock], &t);
    return nsecs_t(t.tv_sec)*1000000000LL + t.tv_nsec;
}

来源

nanoTime()执行

System.nanoTime()也被实现为用 注释的本机方法@CriticalNative

public final class System {
    @CriticalNative
    public static native long nanoTime();
}

来源

本机实现位于System.c

static jlong System_nanoTime() {
  struct timespec now;
  clock_gettime(CLOCK_MONOTONIC, &now);
  return now.tv_sec * 1000000000LL + now.tv_nsec;
}

来源

这两个实现其实很类似,都是调用clock_gettime()

事实证明,最近@CriticalNative才被添加,这解释了为什么它更慢!System.nanoTime()

结论

在跟踪生产应用程序中的性能时:

  • 对于大多数用例来说,毫秒分辨率就足够了。
  • 要测量时间间隔,请使用SystemClock.uptimeMillis()System.nanoTime()。后者在旧版 Android 上速度较慢,但​​这在这里无关紧要。
  • 我更喜欢SystemClock.uptimeMillis(),因为我更容易与毫秒相关。
    • 100 毫秒是人类不再感觉自己在直接操作 UI 中的对象(即拥有“直观”体验)的极限,而是开始感觉自己在命令计算机为他们执行操作,然后等待答案(来源
    • 100毫秒是十分之一秒,这很容易记住。但我对纳秒没有同样的快速参考框架,我必须记住1毫秒=1,000,000纳秒,然后再进行计算。
  • SystemClock不在 JDK 中,因此如果您正在编写可移植代码,那么System.nanoTime()就可以了。
鏂囩珷鏉ユ簮锛�https://dev.to/pyricau/android-vitals-what-time-is-it-2oih
PREV
什么是 HTTP 103?
NEXT
学习 SVG 动画来丰富我的 GitHub 个人资料