记一次Flutter DateTime.now().toIso8601String()遇到的坑

业务背景

项目中用到了fish-redux框架,state里用到了通过DateTime.now().toIso8601String()为列表里是每个State赋值uniqueId,这个id是要求唯一性。

了解fish-redux的应该知道在列表里reduce更新state的时候需要判断state的uniqueId是否相同,只有uniqueId相等才能认为是同1个state。

具体Demo可以参考fish-redux todo_compoent下的state实现

现象

项目列表使用了第三方瀑布流库flutter_staggered_grid_view ,结果陆陆续续有反馈在相邻的位置会刷到一模一样的卡片,类似于下图这样

image

原因:UniqueIdState类的uniqueId生成方法引用了DateTime.now().toIso8601String(),而flutter内部的这个方法在iPhone 11 Pro Max上会有重复现象,虽然通过下图源码看,生成的字符串已经精确到微秒级,但还是有概率生成两个一模一样的String,怀疑是因为微秒取的是前三位,不得不说iphone 11 pro max的cpu性能真好,android上就没出过这样的问题。

image

但通过加埋点日志看,数据是有重复的,埋点的截图如下,第三个和第四个卡片的uniqueId是一样的,注意看uuid的最后六位数200307,说明微秒的值已经写入进去了。


image.png
解决: 引入第三方库 Uuid

Uuid生成随机数的方法主要有两种

1. Uuid().v1(); //引用的也是时间戳,遂放弃
var clockSeq = (options['clockSeq'] != null) ? options['clockSeq'] : _clockSeq;

    // UUID timestamps are 100 nano-second units since the Gregorian epoch,
    // (1582-10-15 00:00). Time is handled internally as 'msecs' (integer
    // milliseconds) and 'nsecs' (100-nanoseconds offset from msecs) since unix
    // epoch, 1970-01-01 00:00.
    var mSecs = (options['mSecs'] != null) ? options['mSecs'] : (DateTime.now()).millisecondsSinceEpoch;//注意这里,引用的也是时间戳,遂放弃

    // Per 4.2.1.2, use count of uuid's generated during the current clock
    // cycle to simulate higher resolution clock
    var nSecs = (options['nSecs'] != null) ? options['nSecs'] : _lastNSecs + 1;

    // Time since last uuid creation (in msecs)
    var dt = (mSecs - _lastMSecs) + (nSecs - _lastNSecs) / 10000;

    // Per 4.2.1.2, Bump clockseq on clock regression
    if (dt < 0 && options['clockSeq'] == null) {
      clockSeq = clockSeq + 1 & 0x3fff;
    }

    // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new
    // time interval
    if ((dt < 0 || mSecs > _lastMSecs) && options['nSecs'] == null) {
      nSecs = 0;
    }

    // Per 4.2.1.2 Throw error if too many uuids are requested
    if (nSecs >= 10000) {
      throw Exception('uuid.v1(): Can\'t create more than 10M uuids/sec');
    }

    _lastMSecs = mSecs;
    _lastNSecs = nSecs;
    _clockSeq = clockSeq;

    // Per 4.1.4 - Convert from unix epoch to Gregorian epoch
    mSecs += 12219292800000;

    // 一堆位运算
   return unparse(buf);
2. Uuid().v4(); 基于随机算法实现,最后用的v4
options = (options != null) ? options : Map<String, dynamic>();

// Use the built-in RNG or a custom provided RNG

var positionalArgs = (options['positionalArgs'] != null) ? options['positionalArgs'] : [];

var namedArgs =

    (options['namedArgs'] != null) ? options['namedArgs'] as Map<Symbol, dynamic> : const <Symbol, dynamic>{};

var rng = (options['rng'] != null) ? Function.apply(options['rng'], positionalArgs, namedArgs) : _globalRNG();//随机生成了16个double

// Use provided values over RNG

var rnds = (options['random'] != null) ? options['random'] : rng;

// per 4.4, set bits for version and clockSeq high and reserved

rnds[6] = (rnds[6] & 0x0f) | 0x40;

rnds[8] = (rnds[8] & 0x3f) | 0x80;

return unparse(rnds);

_globalRNG()的默认实现

var rand, b = List<int>(16);

    var _rand = (seed == -1) ? Random() : Random(seed);
    //生成了16个随机的double
    for (var i = 0; i < 16; i++) {
      if ((i & 0x03) == 0) {
        rand = (_rand.nextDouble() * 0x100000000).floor().toInt();
      }
      b[i] = rand >> ((i & 0x03) << 3) & 0xff;
    }

    return b;

既然要使用,当然要做下性能对比
image.png

总结 测试次数在1万到十万之间可以看到平均值比较稳定,可以作为实际的参考值。uuid的性能相对于DateTime.now().toIso8601String() 大概还是有20倍的差距,但考虑到业务列表的情况,最多不会一次性创建超过30个,所以可以使用v4不会有大的性能损耗。(ps 三星s7是16年的手机,iphone6是14年的机子,但从测试结果看,同样的代码iphone6比三星s7要快好多,苹果爸爸nb)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343