You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

180 lines
16 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 04 | Flutter区别于其他方案的关键技术是什么
你好,我是陈航。
Flutter是什么它出现的动机是什么解决了哪些痛点相比其他跨平台技术Flutter的优势在哪里……相信很多人在第一眼看到Flutter时都会有类似的疑问。
别急在今天的这篇文章中我会与你介绍Flutter的历史背景和运行机制并以界面渲染过程为例与你讲述其实现原理让你对Flutter能够有一个全方位的认知和感受。在对Flutter有了全面了解后这些疑问自然也就迎刃而解了。
接下来我们就从Flutter出现的历史背景开始谈起吧。
## Flutter出现的历史背景
为不同的操作系统开发拥有相同功能的应用程序,开发人员只有两个选择:
1. 使用原生开发语言即Java和Objective-C针对不同平台分别进行开发。
2. 使用跨平台解决方案,对不同平台进行统一开发。
原生开发方式的体验最好但研发效率和研发成本相对较高而跨平台开发方式研发虽然效率高但为了抹平多端平台差异各类解决方案暴露的组件和API较原生开发相比少很多因此研发体验和产品功能并不完美。
所以最成功的跨平台开发方案其实是依托于浏览器控件的Web。浏览器保证了99%的概率下Web的需求都是可以实现的不需要业务将就“技术”。不过Web最大的问题在于它的性能和体验与原生开发存在肉眼可感知的差异因此并不适用于对体验要求较高的场景。
对于用户体验更接近于原生的React Native对业务的支持能力却还不到浏览器的5%仅适用于中低复杂度的低交互类页面。面对稍微复杂一点儿的交互和动画需求开发者都需要case by case地去review甚至还可能要通过原生代码去扩展才能实现。
这些因素,也就导致了虽然跨平台开发从移动端诞生之初就已经被多次提及,但到现在也没有被很好地解决。
带着这些问题我们终于迎来了本次专栏的主角——Flutter。
Flutter是构建Google物联网操作系统Fuchsia的SDK主打跨平台、高保真、高性能。开发者可以通过 Dart语言开发App一套代码可以同时运行在 iOS 和 Android平台。 Flutter使用Native引擎渲染视图并提供了丰富的组件和接口这无疑为开发者和用户都提供了良好的体验。
从2017年5月谷歌公司发布的了Alpha版本的Flutter到2018年底Flutter Live发布的1.0版本再到现在最新的1.5版本截止至2019年7月1日Flutter正在赢得越来越多的关注。
很多人开始感慨跨平台技术似乎终于迎来了最佳解决方案。那么接下来我们就从原理层面去看看Flutter是如何解决既有跨平台开发方案问题的。
## Flutter是怎么运转的
与用于构建移动应用程序的其他大多数框架不同Flutter是重写了一整套包括底层渲染逻辑和上层开发语言的完整解决方案。这样不仅可以保证视图渲染在Android和iOS上的高度一致性即高保真在代码执行效率和渲染性能上也可以媲美原生App的体验即高性能
就是Flutter和其他跨平台方案的本质区别
* React Native之类的框架只是通过JavaScript虚拟机扩展调用系统组件由Android和iOS系统进行组件的渲染
* Flutter则是自己完成了组件渲染的闭环。
那么,**Flutter是怎么完成组件渲染的呢**?这需要从图像显示的基本原理说起。
在计算机系统中图像的显示需要CPU、GPU和显示器一起配合完成CPU负责图像数据计算GPU负责图像数据渲染而显示器则负责最终图像显示。
CPU把计算好的、需要显示的内容交给GPU由GPU完成渲染后放入帧缓冲区随后视频控制器根据垂直同步信号VSync以每秒60次的速度从帧缓冲区读取帧数据交由显示器完成图像显示。
操作系统在呈现图像时遵循了这种机制而Flutter作为跨平台开发框架也采用了这种底层方案。下面有一张更为详尽的示意图来解释Flutter的绘制原理。
![](https://static001.geekbang.org/resource/image/95/2a/95cb258c9103e05398f9c97a1113072a.png)
图1 Flutter绘制原理
可以看到Flutter关注如何尽可能快地在两个硬件时钟的VSync信号之间计算并合成视图数据然后通过Skia交给GPU渲染UI线程使用Dart来构建视图结构数据这些数据会在GPU线程进行图层合成随后交给Skia引擎加工成GPU数据而这些数据会通过OpenGL最终提供给GPU渲染。
在进一步学习Flutter之前我们有必要了解下构建Flutter的关键技术即Skia和Dart。
## Skia是什么
要想了解Flutter你必须先了解它的底层图像渲染引擎Skia。因为Flutter只关心如何向GPU提供视图数据而Skia就是它向GPU提供视图数据的好帮手。
Skia是一款用C++开发的、性能彪悍的2D图像绘制引擎其前身是一个向量绘图软件。2005年被Google公司收购后因为其出色的绘制表现被广泛应用在Chrome和Android等核心产品上。Skia在图形转换、文字渲染、位图渲染方面都表现卓越并提供了开发者友好的API。
因此架构于Skia之上的Flutter也因此拥有了彻底的跨平台渲染能力。通过与Skia的深度定制及优化Flutter可以最大限度地抹平平台差异提高渲染效率与性能。
底层渲染能力统一了上层开发接口和功能体验也就随即统一了开发者再也不用操心平台相关的渲染特性了。也就是说Skia保证了同一套代码调用在Android和iOS平台上的渲染效果是完全一致的。
## 为什么是Dart
除了我们在第2篇预习文章“[预习篇 · Dart语言概览](https://time.geekbang.org/column/article/104071)”中提到的Dart因为同时支持AOT和JIT所以具有运行速度快、执行性能好的特点外Flutter为什么选择了Dart而不是前端应用的准官方语言JavaScript呢这个问题很有意思但也很有争议。
很多人说选择Dart是Flutter推广的一大劣势毕竟多学一门新语言就多一层障碍。想想Java对AndroidJavaScript对NodeJS的推动如果换个语言可能就不一样了。
但,**Google公司给出的原因很简单也很直接**Dart语言开发组就在隔壁对于Flutter需要的一些语言新特性能够快速在语法层面落地实现而如果选择了JavaScript就必须经过各种委员会和浏览器提供商漫长的决议。
事实上Flutter的确得到了兄弟团队的紧密支持。2018年2月发布的Dart 2.02018年12月发布的Dart 2.12019年2月发布的Dart 2.22019年5月发布的Dart2.3每次发布都包含了为Flutter量身定制的诸多改造比如改进的AOT性能、更智能的类型隐式转换等
当然Google公司选择使用Dart作为Flutter的开发语言我想还有其他更有说服力的理由
1. Dart同时支持即时编译JIT和事前编译AOT。在开发期使用JIT开发周期异常短调试方式颠覆常规支持有状态的热重载而发布期使用AOT本地代码的执行更高效代码性能和用户体验也更卓越。
2. Dart作为一门现代化语言集百家之长拥有其他优秀编程语言的诸多特性比如完善的包管理机制。也正是这个原因Dart的学习成本并不高很容易上手。
3. Dart避免了抢占式调度和共享内存可以在没有锁的情况下进行对象分配和垃圾回收在性能方面表现相当不错。
Dart是一门优秀的现代语言最初设计也是为了取代JavaScript成为Web开发的官方语言。竞争对手如此强劲最后的结果可想而知。这也是为什么相比于其他热门语言Dart的生态要冷清不少的原因。
而随着Flutter的发布Dart开始转型其自身定位也发生了变化专注于改善构建客户端应用程序的体验因此越来越多的开发者开始慢慢了解、学习这门语言并共同完善它的生态。凭借着Flutter的火热势头辅以Google强大的商业运作能力相信转型后的Dart前景会非常光明。
## Flutter的原理
在了解了Flutter的基本运作机制后我们再来深入了解一下Flutter的实现原理。
首先我们来看一下Flutter的架构图。我希望通过这张图以及对应的解读你能在开始学习的时候就建立起对Flutter的整体印象能够从框架设计和实现原理的高度去理解Flutter区别其他跨平台解决方案的关键所在为后面的学习打好基础而不是直接一上来就陷入语言和框架的功能细节“泥潭”而无法自拔。
![](https://static001.geekbang.org/resource/image/ac/2f/ac7d1cec200f7ea7cb6cbab04eda252f.png)
图2 Flutter架构图
备注:此图引自[Flutter System Overview](https://flutter.dev/docs/resources/technical-overview)
Flutter架构采用分层设计从下到上分为三层依次为Embedder、Engine、Framework。
* Embedder是操作系统适配层实现了渲染Surface设置线程设置以及平台插件等平台相关特性的适配。从这里我们可以看到Flutter平台相关特性并不多这就使得从框架层面保持跨端一致性的成本相对较低。
* Engine层主要包含Skia、Dart和Text实现了Flutter的渲染引擎、文字排版、事件处理和Dart运行时等功能。Skia和Text为上层接口提供了调用底层渲染和排版的能力Dart则为Flutter提供了运行时调用Dart和渲染引擎的能力。而Engine层的作用则是将它们组合起来从它们生成的数据中实现视图渲染。
* Framework层则是一个用Dart实现的UI SDK包含了动画、图形绘制和手势识别等功能。为了在绘制控件等固定样式的图形时提供更直观、更方便的接口Flutter还基于这些基础能力根据Material和Cupertino两种视觉设计风格封装了一套UI组件库。我们在开发Flutter的时候可以直接使用这些组件库。
接下来,我**以界面渲染过程为例和你介绍Flutter是如何工作的。**
页面中的各界面元素Widget以树的形式组织即控件树。Flutter通过控件树中的每个控件创建不同类型的渲染对象组成渲染对象树。而渲染对象树在Flutter的展示过程分为四个阶段布局、绘制、合成和渲染。
### 布局
Flutter采用深度优先机制遍历渲染对象树决定渲染对象树中各渲染对象在屏幕上的位置和尺寸。在布局过程中渲染对象树中的每个渲染对象都会接收父对象的布局约束参数决定自己的大小然后父对象按照控件逻辑决定各个子对象的位置完成布局过程。
![](https://static001.geekbang.org/resource/image/f9/00/f9e6bbf06231fbad54ed11ef291e8d00.png)
图3 Flutter布局过程
为了防止因子节点发生变化而导致整个控件树重新布局Flutter加入了一个机制——布局边界Relayout Boundary可以在某些节点自动或手动地设置布局边界当边界内的任何对象发生重新布局时不会影响边界外的对象反之亦然。
![](https://static001.geekbang.org/resource/image/42/de/42961a84ecc8181d1afe7ffbaa1a55de.png)
图4 Flutter布局边界
### 绘制
布局完成后渲染对象树中的每个节点都有了明确的尺寸和位置。Flutter会把所有的渲染对象绘制到不同的图层上。与布局过程一样绘制过程也是深度优先遍历而且总是先绘制自身再绘制子节点。
以下图为例节点1在绘制完自身后会再绘制节点2然后绘制它的子节点3、4和5最后绘制节点6。
![](https://static001.geekbang.org/resource/image/8c/b8/8c1d612990d9ada0508c5a41c9e4cab8.png)
图5 Flutter 绘制示例
可以看到由于一些其他原因比如视图手动合并导致2的子节点5与它的兄弟节点6处于了同一层这样会导致当节点2需要重绘的时候与其无关的节点6也会被重绘带来性能损耗。
为了解决这一问题Flutter提出了与布局边界对应的机制——重绘边界Repaint Boundary。在重绘边界内Flutter会强制切换新的图层这样就可以避免边界内外的互相影响避免无关内容置于同一图层引起不必要的重绘。
![](https://static001.geekbang.org/resource/image/da/ee/da430de8f265f444c4801758902a8bee.png)
图6 Flutter重绘边界
重绘边界的一个典型场景是Scrollview。ScrollView滚动的时候需要刷新视图内容从而触发内容重绘。而当滚动内容重绘时一般情况下其他内容是不需要重绘的这时候重绘边界就派上用场了。
### 合成和渲染
终端设备的页面越来越复杂因此Flutter的渲染树层级通常很多直接交付给渲染引擎进行多图层渲染可能会出现大量渲染内容的重复绘制所以还需要先进行一次图层合成即将所有的图层根据大小、层级、透明度等规则计算出最终的显示效果将相同的图层归类合并简化渲染树提高渲染效率。
合并完成后Flutter会将几何图层数据交由Skia引擎加工成二维图像数据最终交由GPU进行渲染完成界面的展示。这部分内容我已经在前面的内容中介绍过这里就不再赘述了。
接下来我们再看看学习Flutter都需要学习哪些知识。
## 学习Flutter需要掌握哪些知识
终端设备越来越碎片化需要支持的操作系统越来越多从研发效率和维护成本综合考虑跨平台开发一定是未来大前端的趋势我们应该拥抱变化。而Flutter提供了一套彻底的移动跨平台方案也确实弥补了如今跨平台开发框架的短板解决了业界痛点极有可能成为跨平台开发领域的终极解决方案前途非常光明。
那么,**我们学习Flutter都需要掌握哪些知识呢**
我按照App的开发流程开发、调试测试、发布与线上运维将Flutter的技术栈进行了划分里面几乎包含了Flutter开发需要的所有知识点。而这些所有知识点我会在专栏中为你一一讲解。掌握了这些知识点后你也就具备了企业级应用开发的必要技能。
这些知识点,如下图所示:
![](https://static001.geekbang.org/resource/image/99/64/9959006fe52706a123cc7fc596346064.jpg)
图7 Flutter知识体系
有了这张图你是否感觉到学习Flutter的路线变得更加清晰了呢
## 小结
今天我带你了解了Flutter的历史背景与运行机制并以界面渲染过程为例从布局、绘制、合成和渲染四个阶段讲述了Flutter的实现原理。此外我向你介绍了构建Flutter底层的关键技术Skia与Dart它们是Flutter有别于其他跨平台开发方案的核心所在。
最后我梳理了一张Flutter学习思维导图围绕一个应用的迭代周期介绍了Flutter相关的知识点。我希望通过这个专栏能和你把Flutter背后的设计原理和知识体系讲清楚让你能对Flutter有一个整体感知。这样在你学完这个专栏以后就能够具备企业级应用开发的理论基础与实践。
## 思考题
你是如何理解Flutter的三大特点跨平台、高保真、高性能的你又打算怎么学习这个专栏呢
欢迎你在评论区给我留言分享你的观点,我会在下一篇文章中等待你!感谢你的收听,也欢迎你把这篇文章分享给更多的朋友一起阅读。