Flutter之美

发表于 2年以前  | 总阅读数:2837 次

❝本文旨在尽量避开具体的代码细节,从思想上去介绍flutter的各种技术实现,让已经在从事flutter开发的同学有更多的收获,同时对flutter感兴趣的观望者也能更好的了解这门技术

一、flutter能给我们带来什么?

跨越多个平台的能力

由于不依赖平台,使用独立渲染的方式,可以在多个平台上高效运行:

android、ios:

所有ui部分的开发都可以完全独立进行,同时具备与原生平台互调用的能力,通过制作插件的方式,可以完美使用现有两个移动平台的生态资源,大部分轮子不需要再重新制作

windows、mac、linux:

由于native的相似性,移动平台的插件大部分都可以兼容pc平台,把移动端代码迁移到桌面端非常容易,一套app的代码可以在桌面端运行,这成本真的太低了,太吸引人了

web:

要将dart代码生成js代码,打包体积比较大,生态资源也不如js,用到native特性的插件都不支持web,代码共用困难,目前很少使用

Fuchsia:

Google自研的物联网操作系统,基于Zircon微内核(非linux),未来2年内大概率面世。它将flutter作为框架层,相信未来大面积使用这个操作系统的时候,flutter技术也会迎来一个新的高潮

开发的时间成本大大降低

强大的跨平台能力,已经大大节约了我们开发的人力时间成本,但flutter的强大远不止如此,下面我来一一介绍

热重载:

同rn一样,拥有jit即时编译的能力,可以在调试时无需重新运行,修改代码后可以立马同步到界面。想想android开发中,特别大型的项目,编译超过3分钟的不少见,每天几十次的编译运行能节省出大概1小时以上的时间,可以早早打开下班啦

代码一键定位:

先来看看android开发者的一个痛点,在不熟悉代码的情况下,如果需要改个ui的话,可能需要以下几个步骤:

  1. 首先要定位是哪个activity,通过命令或者layout inspector工具。
  2. 知道是哪个 activity之后,打开对应的xml布局配置文件,如果design里不能明显看出来,就要通过id的命名去找
  3. 如果这个组件就是在activity里,那就可以直接找到id对应的组件了。如果这个组件不在activity里,比如在RecyclerView里,那就需要找到它对应的viewholder,继续在布局里找。
  4. 如果是代码生成的组件,那么就抱歉了,只能寄希望于代码量不是很大了...

好吧,真的是一言难尽

再来看看flutter的一键定位功能:

  1. 点击开启视图定位模式

  1. 点击想要定位的ui组件

  1. 成功定位到该组件的代码位置

是不是又可以提前下班的时间了!!!

高效的dart语言:

我个人认为dart语言可能是移动端开发最好的语言了,下面就说说它好在哪

  1. 拥有java一样的强类型特性,是一款类型安全的语言,并且支持dynamic类型,需要的话,可以像js一样灵活
  2. 支持函数式编程,代码更为简洁
  3. mixin方式实现多继承,比内部类更为优雅
  4. Flutter2.0开始支持空安全,不需要再到处判空了

基本上借鉴了java、js、kotlin的优点,开发效率会得到很大的提升

好了,现在我们知道了flutter相较于传统移动开发的强大之处,后面我们将详细介绍flutter的设计原理和机制,包括整体架构、线程模型、渲染过程等

二、Flutter框架全景

Dart 框架层(Framework)

上层框架,主要包括 dart 侧 Widget 管理、绘制、动画、手势等接口

C++ 引擎层(Engine)

虚拟机、线程模型、与平台的通信、绘制流程、系统事件、文字布局、帧渲染管线等

平台相关的嵌入层(Embeder)

渲染图层、平台线程和事件循环管理,Native Plugin 等

三、flutter的界面渲染过程

视图树的构建流程

flutter中的视图树借鉴了react的思想,也和android中的mvvm类似,核心思想就是ui和数据绑定,数据变化之后重新构建ui,来达到ui更新的目的。实现这种方式构建ui有两个必要条件,一是要比较两次view树变化的区域,这个只需要完成对应的diff算法就很容易解决。二是需要频繁创建、销毁view树的配置对象,需要在内存管理方面做到高效,后面会讲到dart虚拟机是如何应对这种频繁gc的情况。下面让我们来看看具体的ui构建过程

1. 声明式创建widget树:

widget树就是一份简单的、轻量级的配置信息,并不是真正的视图组件节点。它是不可变的,不可修改的,为什么呢?想想我们在android开发里,每一个ui组件可以在xml布局文件里创建,又可以在代码中随意修改,这样造成的结果就是如果你在设备上看到一个组件被修改了,那么在xml文件里不一定能找到修改的出处,同样在代码里也要去找很久,因为view的引用可以被随意传递,这实在太可怕了,这太不可控了,太不利于维护了。

Flutter怎么做的?

Flutter使用声明式构建ui,完全解决了这个痛点,widget不能修改,只能重新声明去更新它,这也是它为什么是轻量级的,重建的代价不大。如果被修改了,从声明处开始寻找,结合一键定位,可以快速找到修改它的出处。

2. 生成element树

这就是真正的视图组件节点了,它和widget树一一对应,会比较新的widget树和原来widget树的变化,只更新变化的节点。

3. 根据element生成的RenderObject树进行渲染。

需要新创建的节点它会将配置信息解析出RenderObject并持有它,用来处理具体的布局和绘制。需要更新的节点则只需要修改RenderObject,不需要重新创建。由此实现了widget树变化后进行最小范围的处理,性能由此得到提升。RenderObject不和上面两棵树一一对应,它只是具体要渲染的节点,比如StatelessWidget只是组合了其他widget,不需要为他单独生成一个RenderObject

布局与绘制

1. 布局

先回想一下android中的布局:测量一般会进行2次,第一次进行模糊测量,第二次根据子view大小确定具体的测量值,然后布局。一旦其中一个view有了变化,又需要重新布局。它的缺点显而易见:多次测量,view变化后影响较大。来看看flutter是如何优化这两个不足之处的吧

每个节点都有一个布局约束,即maxWidth,minWidth,maxHeight,minHeight,这个约束是根据父节点的约束和自己本身的约束得到的。这样就只需要一次后续遍历便可以确定每一个view的大小和位置。如此,就实现了单次布局

使用RelayoutBoundary进行布局边界限制,边界内的组件发生变化,边界外不重新布局

2. 绘制

为了避免没必要的重绘,每一个RenderObject都有一个isRepaintBoundary属性,即绘制边界,通过这个边界来进行绘制区域的隔断

重绘标记

如图,节点4被标记为需要重新绘制,它的isRepaintBoundary=false,会向上查找,直到找到节点2的isRepaintBoundary=true,将节点2加入到重绘列表中,即真正进行重绘的节点

绘制

如图,节点2存在于重绘制列表中,会进行一个先序遍历2->3->4->5,依次绘制。为什么到5就结束了?因为5也是一个绘制边界,由此确定出最小的绘制区域。节点1和节点6都不进行重新绘制

为什么不是所有节点都使用RepaintBoundary?

RepaintBoundary强制使用新的图层进行绘制,可以避免无关自己的重复绘制。如果图层过多,也会使得渲染性能下降,所以只需要将无需重复绘制的部分使用RepaintBoundary就能做到最大的性能优化

RepaintBoundary应用

 最典型的应用就是CustomScrollView,在滑动过程中,会不断的重绘子组件,如果我们的子组件中有较为复杂的绘制逻辑,就

3. 合成和渲染

终端设备的页面越来越复杂,因此flutter的渲染树层级通常很多,直接交付给渲染引擎进行多图层渲染,可能会出现大量渲染内容的重复绘制,所以还需要先进行依次图层的合成,即将所有的图层根据大小、层级、透明度等规则计算出最终的显示效果,将相同的图层归类合并,简化渲染树,提高渲染效率。

四、Dart虚拟机原理

单线程模型

所谓单线程模型,就是将任务放在队列中轮询执行,以此来实现异步任务,dart线程中有两个任务队列:microtask queue优先级更高,如果它里面有未处理的事件,会优先从这里取出事件处理。event queue,一般的异步任务都是放在这里的。

为什么要用单线程模型呢?

  1. 前端开发大部分异步任务都是为了等待,比如网络请求的等待,数据库、文件数据读取的等待,IO密集型的任务是不消耗cpu的,为此使用多线程反而浪费资源,单线程模型更为合理
  2. 单线程模型里不需要多线程共享内存,就不必担心同步死锁这些问题,开发效率得到提升
  3. 无锁的内存分配是可以实现内存的线性分配的,不用查找可用内存空间,内存分配的效率得到提升

异步任务理解

如何使用异步任务

使用Future传入一个方法就会把一个任务放在Event Queue中了,当这个异步任务执行完毕后,会将Future.then()里的函数添加到MicroTask Queue中,由此可以更优先去处理异步任务的结果

void main() {
  Future(() {
    print("future1");
  });

  Future future2 = Future(() {});

  Future(() {
    print("future3-1");
  }).then((value) {
    print("future3-2");
    scheduleMicrotask(() {
      print("future3-microtask");
    });
  }).then((value) {
    print("future3-3");
  });

  Future(() {
    print("future4-1");
  }).then((value) {
    Future(() {
      print("future4-2");
    });
  }).then((value) {
    print("future4-3");
  });

  future2.then((value) {
    print("future2-1");
  });

  scheduleMicrotask(() {
    print("microtask 1");
  });

  print("main");
}

输出结果为:

main
microtask 1
future1
future2-1
future3-1
future3-2
future3-3
future3-microtask
future4-1
future4-3
future4-2
  • Event Loop 优先执行 main 方法同步任务,再执行微任务,最后执行 Event Queue 的异步任务。所以 main先执行
  • 同理微任务 microtask 1 执行
  • 其次,Event Queue FIFO,future1 被执行
  • future2 内部为空,所以 then 里的内容被加到微任务队列中去,微任务优先级最高,所以 future2-1 被执行
  • 其次,future3-1 被执行。由于存在2个 then,先执行第一个 then 中的 future3-2,然后遇到微任务,所以 future3-microtask 被添加到微任务队列中去,等待下一次 Event Loop 到来时触发。接着执行第二个 then 中的 future3-3。随着下一次 Event Loop 到来,future3-microtask 被执行
  • 其次,future4-1 被执行。随后的第一个 then 中的任务又是被 Future 包装成一个异步任务,被添加到 Event Queue 中,第二个 then 中的内容也被添加到 Event Queue 中。
  • 接着,执行 future4-3。本次事件循环结束
  • 等下一轮事件循环到来,打印队列中的 future4-2

async和await

用async修饰函数,表示异步方法,但如果async方法中没有出现await,它仍然是一个同步方法

执行顺序为:a

b

c

main

只有当遇到await时,才会将之后的内容打包成一个Future放入event queue中

执行顺序为:a

main

b

c

Future的其他用法

  • Future.catcheError() 用于处理异常
  • Future.whenComplete() 无论是否发生异常都会进行回调
  • Future.wait() 接收一个Future数组,等待所有异步任务完成
  • Completer 它持有一个future,可以通过Completer.complete()来自己控制future完成,相当于一个一次性使用的listener注册

多线程

单线程模型不是为了替代多线程而存在的,只是为了在大量io密集型场景下进行高效开发所设计的,如果我们遇到了算法密集型任务,继续使用单线程,那么就会导致我们的ui线程卡顿了,所以在算法密集型任务里使用多线程来最大化cpu的利用率是必不可少的。

dart里的线程叫做Isolate,意思为隔离,和他的名字类似,两个Isolate之间是不能共享内存的,是独立的,更像是进程的感觉。前面讲到单线程模型的好处,不必操心同步死锁问题,不用查找可用内存进行无锁的内存分配,所以即便使用多线程,也就不能够进行共享内存了。两个线程之间的通信可以通过管道实现

start() async {
  ReceivePort receivePort = ReceivePort(); // 创建管道
  Isolate isolate = await Isolate.spawn(coding, receivePort.sendPort); // 创建 Isolate,并传递发送管道作为参数
  // 监听消息
  receivePort.listen((message) {

  });
}

内存分配和垃圾回收

内存分配

DartVM的内存分配策略非常简单,创建对象时只需要在现有堆上移动指针,内存增长始终是线性的,省去了查找可用内存段的过程 每个Isolate之间是无法共享内存的,所以这种分配策略可以让Dart实现无锁的快速分配。

垃圾回收

Dart的垃圾回收也采用了多生代算法,新生代在回收内存时采用了 “半空间” 算法,触发垃圾回收时Dart会将当前半空间中的“活跃”对象拷贝到备用空间,然后整体释放当前空间的所有内存 整个过程中Dart只需要操作少量的“活跃”对象,大量的没有引用的“死亡”对象则被忽略,这种多生代无锁垃圾回收器,专门为UI框架中常见的大量Widgets对象创建和销毁优化,非常适合Flutter框架中大量Widget重建的场景.

五、理解Runner

什么是Runner?

通过全景图中可以看到,dart VM中的isolate其实也是被Embedder所分配和管理的,所以Root Isolate也只是flutter运行时的其中一个线程,还有其他的一些线程由engine进行管理,称为Runner

  • UI runner:

负责处理root isolate 中的代码执行、界面布局、绘制、生成 layer tree 等

  • GPU runner:

负责将 layer tree 信息转为 GPU 指令,配置绘制所需资源

  • IO runner:

配合 GPU runner,主要负责读取图片、解码,上传到 GPU 等耗时操作

  • Platform runner:

负责处理 Engine 与外部的所有交互 ,同时处理平台相关的所有事件,即平台主线程。平台主线程与flutter 的ui线程相互独立,平台线程卡顿不会影响flutter ui界面。

渲染过程中各个Runner之间的逻辑

  1. Root Isolate 需要创建或重新渲染一个frame时,通知engine
  2. engine通过Platform Runner 监听来自GPU Runner的 vsync信号
  3. Platform Runner 收到vsync信号 通知engine,engine通知Root Isolate进行Widget Tree的build以及布局、绘制、合成Layer Tree
  4. Root Isolate 将Layer Tree 交给engine,engine发送给GPU Runner,GPU Runner配置好资源,将Layer Tree生成GPU指令交给GPU进行最后的渲染

六.总结

相信你已经看到了flutter的强大之处,跨平台能力、高效的开发体验、先进的前端框架设计思想。可能唯一的不足就是它实在是太年轻了,但是也能看到在短短不到三年的时间里它所取得的成就,这足够让人兴奋。

现在已经到了flutter 2.5版本,每隔几个月就会有一次大的版本更新,每次的升级都会带来不同的惊喜,Google爸爸确实是对它寄予了厚望,可能也是想尽快为Fuchsia把路铺的更平吧。

Flutter的未来值得期待!

引用

1.https://juejin.cn/post/6844903901641048077

2.https://juejin.cn/post/6974363413942108197

3.https://juejin.cn/post/6890951845729009671

本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/18kiHU_QQHZ10i451ceD3Q

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:6月以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:6月以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:6月以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:6月以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:6月以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:6月以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:6月以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:6月以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:6月以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:6月以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:6月以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:6月以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:6月以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:6月以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:6月以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:6月以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:6月以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:6月以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:6月以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:6月以前  |  398次阅读  |  详细内容 »
 相关文章
如何有效定位Flutter内存问题? 3年以前  |  14696次阅读
Flutter的手势GestureDetector分析详解 4年以前  |  10804次阅读
Flutter插件详解及其发布插件 4年以前  |  10580次阅读
在Flutter中添加资源和图片 5年以前  |  7839次阅读
 目录