zoukankan      html  css  js  c++  java
  • iOS 时间校准解决方案

    背景

    在 iOS 开发中,凡是用到系统时间的,都要考虑一个问题:对时。有些业务是无需对时,或可以以用户时间为准的,比如动画用到的时间、一些日程类应用等。但电商相关的业务大都不能直接使用设备上的时间,而是需要跟服务器校准后的时间,例如:

    • 区间判断:一些优惠促销活动需要在 app 端判断当前是否在活动期间内。如果用户设备时间不准,会给用户错误的信息,导致投诉。
    • 倒计时:各种秒杀、限时促销、未支付订单的失效等的倒计时。如果用户设备时间不准,会带来倒计时结束后刷新页面,状态没变化的问题。可以测试一下电商大厂的 app,任意拨表之后倒计时仍是正确的。
    • 同步:如有数据同步的需求,设备时间不准会造成不能正确判断数据的新旧关系,可能会让旧数据覆盖新数据,造成数据丢失。
    • 请求时间戳:对于分页的数据,为了防止新插入的数据导致翻页时数据错乱,一个常见的解决方案是请求列表时加上时间戳的参数,后台过滤只显示时间戳之后的数据。如果用户设备表慢了,就会显示不出最新的数据,导致新发布内容在列表不出现的情况。

    可以看出,对时这个需求是非常普遍的。不过实现起来并不难,在这里分享一下我们的经验。

    解决方案

    之所以叫解决方案,是因为这个功能不单是 app 端加几行代码,而是前后端配合完成的。大概思路如下:

    1. 后端需要做的:每一个网络请求的返回数据都要带有服务器当前时间戳
    2. app 端的网络框架在网络请求的公共回调处取出时间戳
    3. 将服务器时间与本地时间的差值缓存到本地
    4. 需要使用时间时,使用本地时间和缓存的时间差,算出相应的服务器时间

    网络请求回调

    服务器的时间戳可以加在 response body 里作为公共字段。在我的项目里,因为有少量 get 请求,所以放在了 response header 里。代码类似如下:

    每次网络请求成功时更新时间差的缓存。

    一个小的注意点是,处理 timestamp 最好始终用 long long 类型。因为 timestamp 传统上是以毫秒为单位的(虽然在 iOS 这个奇葩系统里 NSTimeInteval 是以秒为单位),在 32 位系统上 long 和 NSInteger 都存不下,会溢出。当然,现在 32 位系统的设备已经不常见了。

    时间差的缓存

    在更新缓存时,把服务器时间与本地当前的时间差保存在单例里。

    RCDateTimeUtils.m

    需要使用时间时,根据当前时间和缓存过的时间差,计算校准后的时间:

    RCDateTimeUtils.m
    + (NSDate*)currentTime {
        NSDate* serverDate = [NSDate dateWithTimeIntervalSinceNow:[self sharedInstance].timeIntevalDifference];
        return serverDate;
    }
     
    // 以毫秒为单位
    + (long long)currentTimeStamp {
        NSTimeInterval localTime = [[NSDate date] timeIntervalSince1970];
        NSTimeInterval timeDifference = [RCDateTimeUtils sharedInstance].timeIntevalDifference;
     
        return (long long)((localTimeStamp + timeDifference) * 1000);
    }

    使用时只需调用 [RCDateTimeUtils currentTime] 或 [RCDateTimeUtils currentTimeStamp] 即可。

    讨论

      • Q:这样得出的时间准确吗?
        A:会有一定误差。原因在于,服务器返回的时间戳是从服务器开始返回数据的时间,到客户端接收时会有一点延迟。不过对于我们的后台,这个延迟一般 如果对准确性要求更高,可以考虑使用专门的对时接口,不知道国家天文台有没有……
        另外,这种对时的方案只是用于优化 UI 层面的显示,不能防止用户恶意的篡改。要始终记住客户端的时间戳是不可信的,后端业务凡是使用时间都务必用服务器的时间。
      • Q:缓存的时候,为什么只存在单例里,不持久化存储?
        A:这个我也考虑过,主要是觉得再次启动的时候,时间差可能会发生变化,感觉持久化没有太大的必要。如果觉得有必要的话,也可以在 userDefault 里存一份,启动时取出来即可。
  • 相关阅读:
    C#通过模板导出Word(文字,表格,图片) 转载
    转载:mysql新手入门安装配置:mysql 8.0.13 zip安装,初始配置,修改密码(经测试管用)
    转载:MySQL 8.0.19安装教程(windows 64位)
    VS2010上winform打包发布、打包安装程序
    asp.net core mvc权限控制:分配权限
    asp.net core mvc权限控制:权限控制介绍
    实测可用-免费屏幕录制软件下载地址
    Win10激活工具-Win7激活工具-Office激活工具-KMS激活工具汉化版x64下载-实测可用
    C#-WPF实现抽屉式风格主题框架源码-使用MaterialDesignThemes实现WPF炫酷漂亮的效果-提供Demo下载
    C#实现图片暗通道去雾算法-Demo-提供代码实例下载地址
  • 原文地址:https://www.cnblogs.com/zhangrunchao/p/6169095.html
Copyright © 2011-2022 走看看