system.currenttimemillis
谈谈获取系统时间,以及和服务端交互时的时间校验
背景
最近玩旅行青蛙,偶然发现在没有网络情况下,还是可以正常的收三叶草以及在商店买东西。这时候就觉得这个好像是一个单机游戏,那么三叶草收割刷新的逻辑是客户端自己算的咯。然后在联想到时间,java里面一般都是用system.currenttimemillis()这个比较高效的方法获取时间戳,再format成固定格式的时间。
带着好奇心,我手动把手机时间往后调了几个小时,然后三叶草真的就又长出来了,然后又往后调,又长出来了可以割一波了。等到割了好几拨,时间已经调到第三天的时候,觉得差不多了,准备把时间调回正常的,然而。。。
这个当然不服了,马上打开mac进源码看System.currentTimeMillis()这个到底是个什么逻辑,结果是个native方法,这就很难受了。怎么办呢?想到以前写demo的时候没有给INTERNET权限也可以获取时间,所以这个时间肯定不是从网络上获取的。那么如果从手机里面获取,自然就猜可能和手机系统时间有关。
马上写了个demo把当前时间戳格式化后弹出来,然后改变手机系统时间再运行,发现获取到的确实是手机的时间。
之前还一直以为是是从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数 ,我猜到了开头,却没猜到结尾是当前系统时间,naive
结论:
System.currentTimeMillis()获取的是当前的系统时间,不是网络时间。
扩展 实际案例分析:
最近项目有个需求是添加埋点信息,需要客户端把用户点击事件的时间以及其他参数传过去。如果是实时发送,那么也不需要客户端传时间,直接服务器那边获取就好了。
但是如果是定时发送即客户端要做缓存,比如30分钟内收集的数据写在文件里面发过去。定时的好处是客户端可以把相同类型的数据统计,加一个统计次数的参数然后当成一条数据发送,减小数据量。而且就算什么都不统计,由于n条数据只发送一次,也减小了n-1个HTTP head的大小,这也减小了数据包的大小。
这种情况下,时间就必须要客户端来记录真实时间了,不然30分钟发过去,服务器怎么知道是30分钟内哪个时间触发的。客户端记录时间戳一般也是用System.currentTimeMillis(),这样就存在了上面提过的问题如果用户手机的时间本来就不对呢?比如现在是2.12 用户的手机显示的是2.10(可能他自己也没有意识到),这时候直接拿系统时间传到服务器,接口开发肯定是一脸懵逼的。。
这时分析问题产生的原因,是用户的手机时间不对,导致后台无法正确的进行数据分析。那么需要一个标准的时间,后台是老大,服务器说了算,由服务器定期(10分钟)传一个他的时间戳过来,客户端根据系统时间戳和传过来的时间做个差值,如果这个差值没有超过阈值,也就是大家时间都差不多,那么就算这台手机系统时间没问题的,可以直接用。如果差太多,像上面的例子,那么每次传时间的时候,都要算上差值。总之一切以服务端为准,毕竟客户端也只是提供数据的小弟,大数据分析是后台做的。
吐槽:
看起来问题算是解决了,但是心思缜密的同学肯定会发现,服务端10分钟传一个时间戳过来,时间差值是基于传过来这一刻的差值。如果在这10分钟内用户又手动改了系统时间,那么在下一个10分钟重新计算差值来临之前,是不是这段时间传过去的时间还是不对。
答案是肯定的,但是我们增加时间差值逻辑是针对用户自己也不知道他的手机系统时间不正确的情况。如果真的有用户无聊到一直不断更换系统时间,这个可以由客户端判断也可以由后台判断,然后客户端弹出一个view警告:你TMd的是不是显得蛋疼,信不信我从屏幕跳出来,当头就是一个泰拳警告!(纯属吐槽)