Android 中的时间

最近项目中遇到一个上报时间错误的问题。查了一段时间,中间一度怀疑是否是用户修改时间造成的计算错误。然后就了解了一下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()就要根据自己的需求确定了。

系统还提供了几个时间控制相关的工具:

  1. 标准方法Thread.sleep(long millis)Object.wait(long millis)是基于SystemClock.upTimeMillis()的。所以在系统休眠之后它们的回调也会延期,直到系统被唤醒才继续计时。并且这两个同步方法会响应InterruptException,所以在使用它们的时候必须要处理InterruptException异常。
  2. SystemClock.sleep(long millis)Thread.sleep(long millis) 方法是类似的,只不过SystemClock.sleep(long millis) 不响应InterruptException异常。
  3. Handler类的 postDelay()方法也是基于SystemClock.upTimeMillis()方法的。
  4. AlarmManager可以定时发送消息,即使在系统睡眠、应用停止的状态下也可以发送。我们在创建定时事件的时候有两个参数可以选择RTCELAPSED_REALTIME,它们对应的方法就是System.currentTimeMillis() ~ RTCSystemClock.elapsedRealtime() ~ ELAPSED_REALTIME。这样一对应,它们的区别也就非常明显了。

参考文章:

https://developer.android.com/reference/android/os/SystemClock.html

推荐阅读更多精彩内容