曾经的团队是国内最早投入Flutter框架怀抱的团队,后来又有机会负责起了Flutter相关项目,翻回以前写的文章,感慨良多,这是其中的一篇关于这些内容的闲聊。
| 导语Flutter相关的技术资源官网和网友都有过系统且细致的整理,因此这篇文章不是系统的介绍Flutter,而是把我的学习Flutter的一些感受记录分享,内容组织比较零散,想到哪说到哪,欢迎留言讨论。
前言
Dart是Flutter的编程语言,Flutter是Google的UI工具包,用于从单个代码库构建漂亮的本机编译的移动,Web和桌面应用程序。
接下来我们从Dart到Flutter,再到Flutter engine,聊一下我印象深刻的点。
聊·Dart语言:
并发
我一直在项目中反对组员直接使用_beginthread的多线程并发,因为很多人不考虑函数的可重入性,导致埋下许多坑,在我看来多线程弊大于利,于是严格要求大家只能用PostToXxThread的方式使用多线程。而我发现Dart对多线程并发的设计正是我所需要的,彻底解决了多线程加锁互斥的难题,相当于强制使用Post方式,即满足了并发的要求也解决了多线程最让人头疼的线程同步,真是填补了个大坑,Dart干得漂亮!
单线程如何解决IO等耗时操作导致的无响应呢?推荐的做法是async-await方式,总体上Dart的async-await设计和JavaScript以及C#的设计大同小异,使用体验好。async-await的方式到了底层其实就是多线程(也不排除协程),但是如果想要像dart的这种async-await编写体验则必须让语言支持垃圾回收,因为在不支持垃圾回收的语言(如C++)是无法处理后await返回后栈对象或者this被释放的场景,因此类似C++语言目前只有"鸡肋版的async-await"。Dart runtime的线程模型基本和chromium高度一致。
空安全
我们通过C++实现了win/mac的跨平台,但是代码里大量的空指针保护代码让我很难受,有时有人忘写了指针判断导致崩溃也会让人很头疼,而Dart也很好的解决了这个问题:
这篇文章对Dart的空安全讨论非常深入:深入理解空安全 | Dart
在其中看到这段话也非常认同:"代码的健全性极大程度地决定了开发者对于自己的代码是否有自信。一艘 大部分时间 都在飘忽不定的小船,是不足以让你鼓起勇气,驶往公海进行冒险的"。
避空运算符是个非常好用的语法糖,但是也会导致很多逻辑缺失异常处理,降低代码鲁棒性,为此我们建议在项目中避免使用逼空运算符:
Dart不支持反射:
如果需要用到反射能力,可以使用代码生成思路实现,基于此思路也提供了相关的dart package: dart的反射
关于反射,有的语言默认全部方法支持反射,有的语言使用语法糖支持显式反射,而dart用代码生成方式支持反射(或者说不支持反射),确实比较出乎我的意料之外的。这说明2点,第一:dart重视性能和编译优化,第二:代码生成这种方式在dart体系中应多处出现。代码生成确实给了语言很大的扩展空间,dart编译器和运行时抛掉了反射,也可以更加专注于代码生成的优化和执行效率,也意味着坚实的走在类似C++极致性能的道路上(我也感觉是chrome里优化V8性能那波人被JavaScript的动态化折磨得不要不要的,为此在dart果断的砍掉了动态化的特性)。
和其他语言交互:
因为要实现逻辑跨平台,我对Dart语言和其他语言的交互比较关注。Dart毕竟是一种全新的语言,无论设计理念多么先进都会苦于没有丰富的库,因此Dart也更注重和其他语言的通信。
Dart和C语言的交互比较直接:
Dart和Native (java/OC)交互提供了类型安全机制:
Pigeon是一个代码生成器工具,使Flutter和主机平台之间的通信安全,更轻松,更快捷。
果然,代码生成的思路体现在官方的不少库中。这也许也是从chromium里学到的经验吧,chromium里有大量通过脚本生成的代码。
聊·Flutter :
声明式UI
Flutter框架使用的编程范式 (flutter.dev)
Flutter倾向于采取了最新的技术方案,其中UI界面的核心设计模式参考了React对UI开发的探索模型,这一点在Flutter wiki上有多处进行谈论:
对于习惯了传统的UI模型思维方式来说,这种UI开发模式确实需要一段时间的熟悉。不过这种声明式编程方式确实是一种进步的探索,我想从多个方面对此设计进行思考:
- 声明式用户界面对渲染引擎层不友好,需要引擎对其做更多的优化(比如渲染引擎要自己计算哪个widget需要重新绘制),当渲染引擎的理论和实践发展到一定程度后,才会出现声明式用户界面框架。声明式用户界面的引擎也必将选择单独的渲染树的实现方式。
- 声明式用户界面一定程度上简化了UI开发的复杂度,不需要引入额外的静态语言描述界面结构(如web的html,qt的.ui文件),让界面的实现集中一处。是个重要的进步。我认为相关的代码逻辑能集中一处,就可以大大提高可读性(思考一个大需求的实现,改动了100行代码在10个函数好,还是改动了100个函数每个函数改了10行好)。
- 声明式用户界面的开发模式也避免了界面描述文件那种到处插入widget导致bug不易定位的问题。随处可以插入widget的开发方式自然是灵活的,但是灵活换来的代价是代码设计质量无下限,目前来看,编程的主流的方向是限制灵活性,走向约束和规范。
- 由于不需要解析界面描述文件,声明式用户界面首帧速度更优 。客户端和web都能做UI界面,客户端比web最核心的2个优势就是首帧画面速度和内存占用。如果这两个优势没有了,那客户端同学就可以退出GUI层的开发任务了,专注于Console层了。
- 对于界面状态逻辑特别复杂的界面,声明式用户界面的编写规则更容易直观体现出界面和状态之间的关系,即 UI = f (state) 中的f是单独表达的。声明式用户界面的思维模型让界面里的状态和UI表达更为直观。
- 对于界面状态不在同一个widget的场景,或者界面状态离widget距离较远时,声明式用户界面反而比较麻烦。在实际的开发中,界面状态的数据大部分是放在比较集中的地方(如根widget),而界面小部件widget则是分散到界面树上,这就导致一个问题,实际开发中flutter的build的界面声明会特别巨大。为此flutter提供了InheritedWidget 的特殊widget供孩子共享自己的状态,避免某一个build过于庞大。因此一个复杂界面的主widget都应该从InheritedWidget中派生,避免build函数实现代码的嵌套过于复杂(可以更优雅的使用InheritedWidget:provider | Flutter Package (pub.dev))。但是,InheritedWidget的数据共享是单向的,很多时候确实是不便的,在划分多层次时容易被后续的需求变动推翻,但是单向共享数据的设计是合理的(避免双向依赖)。另一方面,我个人认为有时合理的设计不一定是最佳的设计,如果可能,我会在flutter中实现更方便的state访问机制(引入双向依赖)。
- 说到底,声明式用户界面的设计不少完美的,因此我们在使用flutter 声明式编程范式的时候,要知其然更要知其所以然,在实践中灵活发挥其强大的威力,规避弥补其缺陷和不足。
DevTools是个web页面,但是用dart写的!
看上去和web的devtool看起来很像,感觉就是chromium里的devtool的后端,不过其本身是用dart实现的,这也体现出flutter对dart的开发效率的自信,这也让我更加坚定好好学习flutter。
Fluttre的设计原则:
如果对Flutter的设计原则深刻理解后,就会对Flutter很多细节的实现和方向的制定找到一定的内在联系:
最后再聊一下Flutter Engine。
拉下Flutter engine源码后,粗略看了一下,隐隐的觉得,flutter受chromium项目影响很深,甚至怀疑是同一拨人做出来的。
Build System是GN; 在源码里出现了大量的Copyright 20xx The Chromium Authors的版权声明:
Src目录下的gpu目录就是从chomium里copy出来然后删了不用的文件。
对比了一下flutter和chrome的DEPS文件,核心的库基本是一致的,能找到对应关系:
然后上网上查了一下flutter起源:
Flutter 的诞生其实比较有意思,Flutter 诞生于 Chrome 团队的一场内部实验, 谷歌的前端团队在把前端一些"乱七八糟"的规范去掉后,发现在基准测试里性能居然提高了 20 倍,机缘巧合下 Flutter 就这么被立项。
粗略浏览了一下源码,主要关注lib, runtime, shell三个文件夹即可:
想往flutter里加原生扩展,可以直接在DartIsolate::LoadLibraries添加扩展;想修改VM参数,在DartVM::DartVM构造函数实现。Flutter Engine代码组织清晰,可阅读性很高。让人看一眼就有定制一个的Flutter Engine的冲动。
结语
目前我们的桌面客户端使用C++实现了逻辑跨平台,使用QT实现GUI跨平台。接下来,我们正在计划把APP项目使用Dart实现逻辑跨平台,使用Flutter实现GUI跨平台,最终实现课堂业务的一次开发多平台运行目标。
作为Flutter的初学者,上面很多想法不一定成熟,也期待能尽快成长成Flutter老鸟,思考更多关于Flutter的实践。
参考资料:
Flutter architectural overview | Flutter
带你全面了解 Flutter,它好在哪里?它的坑在哪里? 应该怎么学? - 专栏 - 声网 Agora RTC 开发者社区
Flutter vs Chromium 动画渲染的对比分析 - 知乎 (zhihu.com)
字节跳动为什么选用Flutter:并非跨平台终极之选,但它可能是不一样的未来 - Gityuan博客 | 袁辉辉的技术博客