专栏首页Android技术分享Flutter 事件机制 - Future 和 MicroTask 全解析

Flutter 事件机制 - Future 和 MicroTask 全解析

写在前面

了解过Flutter的同学都知道,不同于 Android 原生开发,dart 是单线程实体的语言,所以我们一般的异步操作,实际上还是通过单线程通过调度任务优先级来实现的,就是我们经常用到的 Future,但是Flutter中的事件机制究竟是怎样的?多个Future 和 Microtask 程序的执行顺序是怎样的? 本文将借助两个比较复杂的例子来详细介绍 Flutter 的事件机制,希望能对大家有所帮助。

Main代码块,EventQueue,MicrotaskQueue 的执行优先级

首先我得提一嘴 isolate(隔离),isolate是有自己的内存和单线程控制的运行实体,isolate类似于线程。 运行中的 Flutter 程序由一个或多个 isolate 组成。我们的代码默认都在 Main isolate中执行。 为了保持高的响应性,特别耗时的任务一般不要放在Main isolate 中。但 isolate 不是本文的重点,在此就不过多赘述。

Dart 中事件机制的实现 :Main isolate 中有一个Looper,但存在两个Queue:Event Queue 和 Microtask Queue 。 因为 isolate 是单线程实体,所以 isolate中的代码是按顺序执行的。 所以 dart 中的代码执行优先级可以分为三个级别:

  1. 在 Main 中写代码将最先执行;
  2. 执行完 Main 中的代码,然后会检查并执行 Microtask Queue 中的任务, 通常使用 scheduleMicrotask 将事件添加到 MicroTask Queue 中;
  3. 最后执行 EventQueue 队列中的代码,通常使用 Future 向 EventQueue加入时间,也可以使用 async 和 await 向 EventQueue 加入事件。

总结:Dart 中事件的执行顺序:Main > MicroTask > EventQueue。 如图:

image.png

验证:

void testSX(){
  new Future(() => print('s_1'));
  scheduleMicrotask(() => print('s_2'));
  print('s_3');
}

输出结果:

I/flutter (32415): s_3
I/flutter (32415): s_2
I/flutter (32415): s_1

Future简介

前面讲到,用 async 和 await 组合,即可向 event queue 中插入 event 实现异步操作,那为什么还会有Future呢? 其实,Future 最主要的功能就是提供了链式调用。

new Future (() => print('拆分任务_1'))
    .then((i) => print('拆分任务_2'))
    .then((i) => print('拆分任务_3'))
    .whenComplete(()=>print('任务完成'));

Future中的 then 并没有创建新的Event丢到Event Queue中,而只是一个普通的Function,在一个 Future 所有的 Function 执行完后,下一个 Future 才会开始执行。

多个 Future 的执行顺序

  1. 规则一:Future 的执行顺序为Future的在 EventQueue 的排列顺序。类似于 JAVA 中的队列,先来先执行。
  2. 规则二:当任务需要延迟执行时,可以使用 new Future.delay() 来将任务延迟执行。
  3. 规则三: Future 如果执行完才添加 than ,该任务会被放入 microTask,当前 Future 执行完会执行 microTask,microTask 为空后才会执行下一个Future。
  4. 规则四:Future 是链式调用,意味着Future 的 then 未执行完,下一个then 不会执行。

理论结束,然后来看一段代码吧:

void testFuture() {
  Future f1 = new Future(() => print('f1'));
  Future f2 = new Future(() =>  null);
  Future f3 = new Future.delayed(Duration(seconds: 1) ,() => print('f2'));
  Future f4 = new Future(() => null);
  Future f5 = new Future(() => null);

  f5.then((_) => print('f3'));
  f4.then((_) {
    print('f4');
    new Future(() => print('f5'));
    f2.then((_) {
      print('f6');
    });
  });
  f2.then((m) {
    print('f7');
  });
  print('f8');
}

各位同学可以试着写一下结果,然后对比下输出结果。

输出结果:

com.example.flutter_dart_app I/flutter: f8
com.example.flutter_dart_app I/flutter: f1
com.example.flutter_dart_app I/flutter: f7
com.example.flutter_dart_app I/flutter: f4
com.example.flutter_dart_app I/flutter: f6
com.example.flutter_dart_app I/flutter: f3
com.example.flutter_dart_app I/flutter: f5
com.example.flutter_dart_app I/flutter: f2

是不是跟自己的结果大相径庭,别急,看我来慢慢分析: 分析:

  1. 首先执行Main 的代码,所以首先输出: 8;
  2. 然后参考上面的规则1,Future 1 到 5 是按初始化顺序放入 EventQueue中,所以依次执行Future 1到5 , 所以输出结果:8,1,7。
  3. 参考规则2,f3 延时执行,一定是在最后一个:8,1,7,…,2。
  4. 在 f4 中,首先输出 f4 :8,1,7,4,…,2。
  5. 在 f4 的 then 的方法块中,新建了Future, 所以新建的 Future 将在 EventQueue尾部,最后被执行:8,1,7,4,…,5,2。
  6. 在 f4 的 then 的方法块中,给 f2 添加了 then ,但此时 f2 已经执行完了,参考规则三,所以 then 中的代码会被放到 microTask 中,在当前 Future 执行完后执行。 因为此时Future f4已经执行完了,所以会处理microTask(microTask优先级高)。结果:8,1,7,4,6,..,5,2。
  7. 此时我们的 EventQueue 中还有 f5,和在 f4 中添加的新的Future。 所以我们的最终结果就是:8,1,7,4,6,3,5,2。

是不是有点理解不了,没事,牢记四个规则,自己再算一遍,相信你就了然于胸了。重要要在脑海里有一个 EventQueue 的队列模型,牢记先进先出。

然后来试一试下一题:

多Future 和 多micTask 的执行顺序

void testScheduleMicrotatsk() {
  scheduleMicrotask(() => print('Mission_1'));

//注释1
  new Future.delayed(new Duration(seconds: 1), () => print('Mission_2'));

//注释2
  new Future(() => print('Mission_3')).then((_) {
    print('Mission_4');
    scheduleMicrotask(() => print('Mission_5'));
  }).then((_) => print('Mission_6'));

//注释3
  new Future(() => print('Mission_7'))
      .then((_) => new Future(() => print('Mission_8')))
      .then((_) => print('Mission_9'));

//注释4
  new Future(() => print('Mission_10'));

  scheduleMicrotask(() => print('Mission_11'));

  print('Mission_12');
}

大家可以先自己试一下,再对照结果~

输出结果:

I/flutter (19025): Mission_12
I/flutter (19025): Mission_1
I/flutter (19025): Mission_11
I/flutter (19025): Mission_3
I/flutter (19025): Mission_4
I/flutter (19025): Mission_6
I/flutter (19025): Mission_5
I/flutter (19025): Mission_7
I/flutter (19025): Mission_10
I/flutter (19025): Mission_8
I/flutter (19025): Mission_9
Syncing files to device MIX 3...
I/flutter (19025): Mission_2

是不是还是没答全对?没关系,很正常,看我慢慢道来: 分析:

  1. 根据 Main > MicroTask > EventQueue。我们首先会得到输出结果:12,1,11。
  2. 注释1 的 Future 是延时执行,所以:12,1,11,…,2。
  3. 注释2 中创建了 Microtask,Microtask会在该Future执行完后执行,所以:12,1,11,4,6,5,…,2。
  4. 重点来了。我们在注释3 的Future 的 then 中新建了Future(输出Mission_8),新建的 Future 将被加到 EventQueue尾部,并且,注释3的Future后续的then将不再执行,因为这个链被阻塞了! 注意对比上一题中的 f4, 上一题中的 f4 是一个 than 方法包裹了代码块。 此时的结果:12,1,11,4,6,5,7,…,2。
  5. 执行完注释4 的 Future,然后会执行我们在注释3 Future 新加入的 Future,之后注释3 的Future不再阻塞,会继续执行,结果: 12,1,11,4,6,5,7,10,8,9,2。

看到这里,相信各位同学已经对 Dart 事件机制有一个大概的了解,希望能对 各位在学Flutter 的同学有所帮助,蟹蟹~

END

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Fragment新功能,setMaxLifecycle了解一下

    看了一篇关于ViewPager2软文后,我发现最新的Fragment代码淘汰了setUserVisibleHint方法,转而支持用setMaxLifecycle...

    Android技术干货分享
  • 不得不看的Flutter与Android混合开发

    记得在flutter刚出来时,笔者就开始学习flutter。但由于当时嫌弃flutter复杂的层级组合且未推出稳定版,所以当时就放弃了深入学习,现如今随着flu...

    Android技术干货分享
  • Android自定义控件:一款多特效的智能loadingView

    画图首先是onDraw方法(我会把圆代码写上,一步一步剖析): 首先在view中定义个属性:private RectF rectf = new RectF();...

    Android技术干货分享
  • SAP S/4HANA里如何创建Customer主数据以及执行后续处理

    1, Launch tcode: BP and select the Organization

    Jerry Wang
  • 央行数字货币设计与物理学四大神兽(1)——芝诺的龟

    人类历史长河中,物理学界曾经流行着四大神兽,分别是缩地成寸永远追不上的芝诺龟,推演万物未卜先知的拉普拉斯鬼,逆转时空起死回生的麦克斯韦妖和超越因果亦生亦死的薛定...

    非哥
  • python 排列组合算法

    def c(n,m,out): if(m==0):    return 1 x=n while x>=m:    out.append(x)    ...

    py3study
  • DigiMarket:货币贬值?现金债券不再安全?数字货币可成替代资产?

    据巴克莱和德意志银行的数据,价值超过15万亿美元的全球债券现在正处于负收益状态。自2018年10月以来,这一统计数据几乎增至三倍。对于一种按照正常经济理论毫无意...

    DigiMarket
  • SAP CRM的订单模型移植到S/4HANA之后,到底做了哪些改进?

    One order model consists of a series of objects with two different types:

    Jerry Wang
  • 追求完美代码之——实现元素拖拽修改宽高和位移插件

    我们如果使用过ppt、keynote,元素的小控件一定少不了,可以实现修改修改宽高和位移,大概是这样

    lhyt
  • (译)发现 Serverless 应用中的隐形成本

    像 AWS Lambda 这样的服务,代码每运行 100 毫秒,就需要支付一定的费用。例如 512 MB 内存的 Lambda 的(每 100 毫秒)费率是 $...

    崔秀龙

扫码关注云+社区

领取腾讯云代金券