最近项目中遇到一个上报时间错误的问题。查了一段时间,中间一度怀疑是否是用户修改时间造成的计算错误。然后就了解了一下Android系统中所使用的时间。其实谷歌已经为我们整理了一份文档并做了区分。可以翻墙的同学直接参考 这里。这里还是根据自己的理解与经验做一些解读。
System.currentTimeMillis()
我们一般通过它来获取手机系统的当前时间。事实上,它返回的值是系统时刻距离标准时刻(1970.01.01 00:00:00)的毫秒数。它相当于家里的“挂钟”一样,并不是十分精准,而且可以随意修改。所以它可能经常被网络或者用户校准。正是由于这个原因,这个方法获取的值不适合用来做时间间隔的统计。但是它适合用来获取当前日期,时刻等时间点相关的逻辑。
SystemClock.upTimeMillis()
这个值记录了系统启动到当前时刻经过的时间。但是系统深度睡眠(CPU睡眠,黑屏,系统等待唤醒)之中的时间不算在内。这个值不受系统时间设置,电源策略等因素的影响,因此它是大多数时间间隔统计的基础,例如Thread.sleep(long millis)
,Object.wait(long millis)
,System.nanoTime()
等。系统保证了这个值只增长不下降,所以它适合所有的不包括系统睡眠时间的时间间隔统计。
SystemClock.elapsedRealtime() & SystemClock.elapsedRealtimeNanos
这个值与SystemClock.upTimeMillis()
类似。它是系统启动到当前时刻经过的时间,包括了系统睡眠经过的时间。在CPU休眠之后,它依然保持增长。所以它适合做更加广泛通用的时间间隔的统计。
综上,如果想要避免用户修改时间,网络校准时间对时间间隔统计的影响,使用SystemClock
类相关的方法就可以了,至于选择upTimeMillis()
还是elapsedRealtime()
就要根据自己的需求确定了。
系统还提供了几个时间控制相关的工具:
- 标准方法
Thread.sleep(long millis)
和Object.wait(long millis)
是基于SystemClock.upTimeMillis()
的。所以在系统休眠之后它们的回调也会延期,直到系统被唤醒才继续计时。并且这两个同步方法会响应InterruptException
,所以在使用它们的时候必须要处理InterruptException
异常。 -
SystemClock.sleep(long millis)
与Thread.sleep(long millis)
方法是类似的,只不过SystemClock.sleep(long millis)
不响应InterruptException
异常。 -
Handler
类的postDelay()
方法也是基于SystemClock.upTimeMillis()
方法的。 -
AlarmManager
可以定时发送消息,即使在系统睡眠、应用停止的状态下也可以发送。我们在创建定时事件的时候有两个参数可以选择RTC
和ELAPSED_REALTIME
,它们对应的方法就是System.currentTimeMillis() ~ RTC
,SystemClock.elapsedRealtime() ~ ELAPSED_REALTIME
。这样一对应,它们的区别也就非常明显了。
参考文章:
https://developer.android.com/reference/android/os/SystemClock.html