美文网首页Flutter学习
记一次Flutter DateTime.now().toIso8

记一次Flutter DateTime.now().toIso8

作者: 余瑜雨鱼 | 来源:发表于2020-04-18 21:57 被阅读0次

业务背景

项目中用到了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)

相关文章

网友评论

    本文标题:记一次Flutter DateTime.now().toIso8

    本文链接:https://www.haomeiwen.com/subject/tcpavhtx.html