Flutter核心技术与实战
陈航
美团点评高级技术专家
立即订阅
6150 人已学习
课程目录
已完结 47 讲
0/4登录后,你可以任选4讲全文学习。
课前必读 (3讲)
开篇词 | 为什么每一位大前端从业者都应该学习Flutter?
免费
01 | 预习篇 · 从0开始搭建Flutter工程环境
02 | 预习篇 · Dart语言概览
Flutter开发起步 (3讲)
03 | 深入理解跨平台方案的历史发展逻辑
04 | Flutter区别于其他方案的关键技术是什么?
05 | 从标准模板入手,体会Flutter代码是如何运行在原生系统上的
Dart语言基础 (3讲)
06 | 基础语法与类型变量:Dart是如何表示信息的?
07 | 函数、类与运算符:Dart是如何处理信息的?
08 | 综合案例:掌握Dart核心特性
Flutter基础 (13讲)
09 | Widget,构建Flutter界面的基石
10 | Widget中的State到底是什么?
11 | 提到生命周期,我们是在说什么?
12 | 经典控件(一):文本、图片和按钮在Flutter中怎么用?
13 | 经典控件(二):UITableView/ListView在Flutter中是什么?
14 | 经典布局:如何定义子控件在父容器中排版的位置?
15 | 组合与自绘,我该选用何种方式自定义Widget?
16 | 从夜间模式说起,如何定制不同风格的App主题?
17 | 依赖管理(一):图片、配置和字体在Flutter中怎么用?
18 | 依赖管理(二):第三方组件库在Flutter中要如何管理?
19 | 用户交互事件该如何响应?
20 | 关于跨组件传递数据,你只需要记住这三招
21 | 路由与导航,Flutter是这样实现页面切换的
Flutter进阶 (17讲)
22 | 如何构造炫酷的动画效果?
23 | 单线程模型怎么保证UI运行流畅?
24 | HTTP网络编程与JSON解析
25 | 本地存储与数据库的使用和优化
26 | 如何在Dart层兼容Android/iOS平台特定实现?(一)
27 | 如何在Dart层兼容Android/iOS平台特定实现?(二)
28 | 如何在原生应用中混编Flutter工程?
29 | 混合开发,该用何种方案管理导航栈?
30 | 为什么需要做状态管理,怎么做?
31 | 如何实现原生推送能力?
32 | 适配国际化,除了多语言我们还需要注意什么?
33 | 如何适配不同分辨率的手机屏幕?
34 | 如何理解Flutter的编译模式?
35 | Hot Reload是怎么做到的?
36 | 如何通过工具链优化开发调试效率?
37 | 如何检测并优化Flutter App的整体性能表现?
38 | 如何通过自动化测试提高交付质量?
Flutter综合应用 (6讲)
39 | 线上出现问题,该如何做好异常捕获与信息采集?
40 | 衡量Flutter App线上质量,我们需要关注这三个指标
41 | 组件化和平台化,该如何组织合理稳定的Flutter工程结构?
42 | 如何构建高效的Flutter App打包发布环境?
43 | 如何构建自己的Flutter混合开发框架(一)?
44 | 如何构建自己的Flutter混合开发框架(二)?
结束语 (1讲)
结束语 | 勿畏难,勿轻略
特别放送 (1讲)
特别放送 | 温故而知新,与你说说专栏的那些思考题
Flutter核心技术与实战
登录|注册

23 | 单线程模型怎么保证UI运行流畅?

陈航 2019-08-20
你好,我是陈航。
在上一篇文章中,我带你一起学习了如何在 Flutter 中实现动画。对于组件动画,Flutter 将动画的状态与渲染进行了分离,因此我们需要使用动画曲线生成器 Animation、动画状态控制器 AnimationController 与动画进度监听器一起配合完成动画更新;而对于跨页面动画,Flutter 提供了 Hero 组件,可以实现共享元素变换的页面切换效果。
在之前的章节里,我们介绍了很多 Flutter 框架出色的渲染和交互能力。支撑起这些复杂的能力背后,实际上是基于单线程模型的 Dart。那么,与原生 Android 和 iOS 的多线程机制相比,单线程的 Dart 如何从语言设计层面和代码运行机制上保证 Flutter UI 的流畅性呢?
因此今天,我会通过几个小例子,循序渐进地向你介绍 Dart 语言的 Event Loop 处理机制、异步处理和并发编程的原理和使用方法,从语言设计和实践层面理解 Dart 单线程模型下的代码运行本质,从而懂得后续如何在工作中使用 Future 与 Isolate,优化我们的项目。

Event Loop 机制

首先,我们需要建立这样一个概念,那就是 Dart 是单线程的。那单线程意味着什么呢?这意味着 Dart 代码是有序的,按照在 main 函数出现的次序一个接一个地执行,不会被其他代码中断。另外,作为支持 Flutter 这个 UI 框架的关键技术,Dart 当然也支持异步。需要注意的是,单线程和异步并不冲突。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Flutter核心技术与实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(31)

  • lk
    Future(() => print('f1'))
          .then((_) async => scheduleMicrotask(() => print('f2')))
          .then((_) => print('f3'));
      Future(() => print('f4'));

    作者回复: 赞👍另外建议把await加上

    2019-08-29
    2
  • GGL
    // 第一段
      Future(() => print('f6'))
        .then((_) => Future(() => print('f7')))
        .then((_) => print('f8'));

    执行结果为:f6 f7 f8

      // 第二段
      Future(() => print('f6'))
      .then((_) {
          Future(() => print('f7'));
        })
      .then((_) => print('f8'));

    执行结果为:f6 f8 f7

    老师,上面这两段代码为什么执行结果不一样呢?

    作者回复: 单行箭头函数是Future,和函数体里有Future不是一回事

    2019-11-03
    1
    1
  • Geek_keyi
    对Dart单线程模型理解还是迷惑,请教一下:假如有一个任务(读写文件或者网络)耗时10秒,并且加入到了事件任务队列中,执行单这个任务的时候不就把线程卡主吗?

    作者回复: 文件I/O和网络调用并不是在Dart层做的,而是由操作系统提供的异步线程,他俩把活儿干完之后把结果刚到队列中,Dart代码只是执行一个简单的读动作。

    2019-09-25
    1
    1
  • Geek_20f143
    Future(() => print('a1'))
            .then((_) {
          Future(() => print('a2'));
        }).then((_) => print('a3'));


    Future(() => print('a1'))
            .then((_) => Future(() => print('a2')))
            .then((_) => print('a3'));

    老师,为什么第一部分打印顺序是a1 a3 a2... 第二部分打印顺序是a1 a2 a3
    a2区别那部分不是只是语法糖吗?
    2019-12-10
  • 满大大
    await 的上下文函数并不包含调用栈,因此 func 后续代码继续执行,打印“func after”。这里怎么理解?
    2019-12-06
  • 菜头
    1、因为两个 SendPort 的作用不一样
    第一个 SendPort 是为了让并发 Isolate 回传一个 SendPort
    第二个 SendPort 是为了让并发 Isolate 回传结果
    可以改造成一个
    监听第一个 SendPort 的回调,根据结果类型复用这个 SendPort
    并且在计算结果完成之后关掉 SendPort 即可

    作者回复: 可以的,赞👍

    2019-11-19
  • 菜头
    老师上面的例子中有一个疑问
    //声明了一个匿名Future,并注册了两个then。第一个then是一个Future

    Future f6 = Future(() => print('f6'))

    f6.then((_) => f7 = Future(() => print('f7')); return f7)
    .then((_) => print('f8'));

    老师这块儿的调用时序为什么不是 f6 f8 ..... f7

    f7 会被放到事件队列里面了 但是f6的第二个 .then(() =>print('8')) 为什么也会被放到事件队列中

    作者回复: 1.你这个代码编译不通过吧
    2.单行箭头函数的then是Future,和函数体内有Future不是一回事

    2019-11-03
  • 菜头
    老师上面的例子中有一个疑问
    //声明了一个匿名Future,并注册了两个then。第一个then是一个Future

    Future f6 = Future(() => print('f6'))

    f6.then((_) => f7 = Future(() => print('f7')); return f7)
    .then((_) => print('f8'));

    老师这块儿的调用时序为什么不是 f6 f8 ..... f7
    Future 执行的 then 不是和 Future 一个事件循环吗

    作者回复: f7这个 then 是一个 Future 异步任务,因此这个 then,以及后续的 then 都被放入到事件队列中了

    2019-11-03
  • jerry
    您好老师,单线程模型是指的事件队列模型,和绘制界面的线程是一个吗

    作者回复: 我们所说的单线程指的是主Isolate。而GPU绘制指令有单独的线程执行,跟主Isolate无关。事实上Flutter提供了4种task runner,有独立的线程去运行专属的任务:
    1.Platform Task Runner:处理来自平台(Android/iOS)的消息
    2.UI Task Runner:执行渲染逻辑、处理native plugin的消息、timer、microtask、异步I/O操作处理等
    3.GPU Task Runner:执行GPU指令
    4.IO Task Runner:执行I/O任务
    除此之外,操作系统本身也提供了大量异步并发机制,可以利用多线程去执行任务(比如socket),我们在主Isolate中无需关心(如果真想主动创建并发任务也可以)

    2019-10-29
  • Geek_869250
    老师可以和JS的event loop做个比较吗

    作者回复: 基本上一样,因为Dart的单线程模型就是借鉴的js

    2019-10-28
  • 巫山老妖
    第一个思考题,是因为isolate是单向通信的,要达到双向通信需要回传一个发送通道,所以需要两个SendPort。

    作者回复: 不对哦,注意看其实我在并发Isolate中发了两次SendPort

    2019-10-18
  • 舒大飞
    老师,还是有点困惑,希望解答下,你说的:文件IO和网络调用并不是在dart层做的,而是由操作系统提供的异步线程,这句话怎么理解,是因为dart里的文件IO和网络调用是这样封装实现的吗?如果不是,那其它语言,比如Android开发中为什么要单独开线程去做IO和网络请求,Android开发中的IO和网络请求不是操作系统提供的异步线程吗

    作者回复: 1.文件I/O和网络调用这类底层服务调用,只是Embedder层做了一次封装,底层有专门的线程处理这些任务;
    2.两者设计理念不同而已,原生Android更底层。你也可以试着增加一层,用线程池模拟出类似Dart的Isolate机制,实现I/O、网络调用的异步化。

    2019-10-03
  • C
    有个疑问:
    Future(() => print("f1"))
      ..then((_) => Future(() => print("f2")))
      ..then((_) => print("f3"));
    Future(() => print("f4"));
    这样打印出来的结果为什么是 f1 f3 f4 f2

    作者回复: Future f = Future(() => print('f1'));
    f.then((_) => Future(() => print('f2')));
    f.then((_) => print('f3'));
    Future(() => print('f4'));
    这么看明白了么

    2019-09-03
    2
  • 楼外楼
    Dart 中的 await 并不是阻塞等待,而是异步等待,这句话是不是有问题,await 就是阻塞后面代码执行,等待异步代码返回。难道我理解有误?

    作者回复: await阻塞的是当前上下文的后续代码执行,并不能阻塞其调用栈上层的后续代码执行

    2019-08-30
  • 小水滴
    还有一个疑问,并发Isolate中可以操作widget吗

    作者回复: 不可以,因为资源完全隔离

    2019-08-29
  • 小水滴
    testIsolate() async{
      print(await asyncFactoriali(4));
    }
    // 同步计算阶乘
    int syncFactorial(n) => n < 2 ? n : n * syncFactorial(n-1);


    // 并发计算阶乘
    Future<dynamic> asyncFactoriali(n) async{
      final response = ReceivePort();// 创建管道
      // 创建并发 Isolate,并传入管道
      await Isolate.spawn(_isolate,response.sendPort);
      response.listen((data){
        if (data is SendPort){
          data.send(n);
        } else {
          print(">>>>>>$data");
        }
      });
    }

    //Isolate 函数体,参数是主 Isolate 传入的管道
    _isolate(initialReplyTo) async {
      final port = ReceivePort();// 创建管道
      initialReplyTo.send(port.sendPort);// 往主 Isolate 回传管道
      final message = await port.first as int;// 等待主 Isolate 发送消息 (参数和回传结果的管道)
      initialReplyTo.send(syncFactorial(message));// 调用同步计算阶乘的函数回传结果
    }

    作者回复: 赞。有个小bug:response没有close掉

    2019-08-29
  • 小水滴
    如果主Isolate创建两个并发Isolate,然后把一个数组分别传递给两个并发Isolate,两个并发Isolate都去操作数组,这样安全吗?

    作者回复: 内存变量在Isolate之间是不共享的,对于你这个case,Isolate之间会对这个数组进行复制

    2019-08-29
    1
  • bo_oc
    Future(() => print('f1'))
            .then((_){ scheduleMicrotask(()=>print('f2')); })
            .then((_){ scheduleMicrotask(()=>print('f3')); });
        Future(() => print('f4'));

    作者回复: f3不需要microtask

    2019-08-29
  • ptlCoder
    像直接使用isolate这种管道机制的场景多不多?个人感觉这种写法繁琐不容易懂

    作者回复: 不多,大多数并发任务的场景通过compute就可以搞定了

    2019-08-28
  • 大土豆
    能否这样认为,dart的这种线程模型既解决了Android/iOS这种多线程机制因为线程抢占等原因导致IO密集型计算不给力,也解决了js这种纯单线程模型导致的CPU密集型计算不给力的问题

    作者回复: 是的,不过js也提供了worker机制,可以做一些相对独立的事情

    2019-08-28
收起评论
31
返回
顶部