System.currentTimeMillis() 的行为是由其设计目的决定的。
核心原因
System.currentTimeMillis() 受系统时间设置影响是因为它的设计初衷是返回当前的“挂钟时间”(Wall Clock Time),也就是我们日常生活中使用的日期和时间。
---
详细解释
1. “挂钟时间”的含义:
· 它表示从 Unix 纪元(1970年1月1日 00:00:00 UTC) 到当前时刻所经过的毫秒数。
· 这个时间应该与墙上的时钟、手机状态栏显示的时间、以及世界上任何地方的标准时间保持一致。
2. 如何同步“挂钟时间”:
· 设备在启动时从硬件时钟(RTC,实时时钟)读取一个初始时间。
· 随后,操作系统会通过多种方式自动或手动校准这个时间:
· 网络时间协议(NTP): 大多数设备默认开启“自动设置时间”,会定期从互联网上的时间服务器同步时间,以确保高度准确。
· 运营商网络: 手机可以从蜂窝网络获取时间信息。
· 用户手动设置: 用户可以在系统设置中随意修改日期和时间。
3. 为什么需要可变的“挂钟时间”?
· 正确反映现实时间: 应用程序需要知道真实的日期和时间,例如:
· 在日历中创建和显示事件。
· 为文件、日志条目、聊天消息打上正确的时间戳。
· 判断证书或授权是否已过期。
· 显示新闻、邮件等的接收时间。
· 适应时区变化: 当你从北京飞到纽约时,你希望手机时间自动从 CST 变为 EST,这个变化就是通过调整 System.currentTimeMillis() 的基准值来实现的。
---
与“单调时钟”的对比
为了更好地理解,我们可以将它与不受系统时间影响的“单调时钟”进行对比:
特性 System.currentTimeMillis() (挂钟时间) SystemClock.elapsedRealtime() (单调时钟)
参考基点 Unix 纪元 (1970-01-01 UTC) 设备上次启动完成的时间
是否会倒退或跳变? 是。可能因NTP同步、用户手动修改、时区切换等原因突然向前或向后跳跃。 否。保证单调递增,只受设备物理时间流逝的影响。
是否受睡眠影响? 否,它独立于设备状态。 elapsedRealtime() 包括深度睡眠时间;uptimeMillis() 不包括。
主要用途 记录事件发生的“绝对时间”(什么时候发生的?) 测量时间间隔、耗时(用了多长时间?)
示例 照片的拍摄日期、日志的生成时间。 动画的持续时间、游戏关卡计时、性能 profiling。
---
会产生问题的场景示例
假设你正在用 System.currentTimeMillis() 来计算一个操作的耗时:
```java
long startTime = System.currentTimeMillis();
// ... 执行一些操作 ...
long endTime = System.currentTimeMillis();
long duration = endTime - startTime; // 计算耗时
```
如果在 startTime 和 endTime 之间,发生了以下任何一件事:
1. 用户手动将时间调快了1小时。
2. 系统自动从NTP服务器同步并修正了时间(可能向前或向后跳变几秒)。
3. 设备穿越了时区,系统时间自动更新。
那么计算出的 duration 将完全错误——它可能是一个巨大的正数、甚至是负数。
结论与最佳实践
· 为什么受影响: System.currentTimeMillis() 的设计目标就是提供与真实世界一致的、可被用户和网络修改的“日历时间”和“时钟时间”。
· 如何选择:
· 当你需要获取当前的日期和时间(例如:显示在UI上、保存到数据库作为记录时间)时,使用 System.currentTimeMillis()。
· 当你需要测量时间间隔、耗时或进行超时判断(例如:动画、游戏循环、性能监测、定时任务)时,必须使用不受系统时间影响的单调时钟,如 SystemClock.elapsedRealtime() 或 System.nanoTime()。
简单来说:问“现在是什么时候?”用 System.currentTimeMillis();问“过了多久?”用 SystemClock.elapsedRealtime()。
1759

被折叠的 条评论
为什么被折叠?



