Flutter
1. Flutter 框架,优缺点
Flutter是 Google 推出的一套开源跨平台 UI 框架,可以快速地在 Android 、iOS和 Web 平台上构建高质量的原生用户界面。同时,Flutter 还是 Google 研发的 Fuchsia 操作系统的默认开发套件。在全世界,Flutter 正在被越来越多的开发者和组织使用,并且 Flutter 是完全免费、开源的。Flutter 采用现代响应式框架构建,其中心思想是使用组件来构建应用的 UI 。当组件的状态发生改变时,组件会重构它的描述,Flutter 会对比之前的描述,以确定底层渲染树从当前状态转换到下一个状态所需要的最小更改。
优点
• 热重载( Hot Reload ),利用 Android Studio 直接一个 ctrl+s 就可以保存并重载,模拟器立马就可以看见效果,相比原生冗长的编译过程强很多;
• 一切皆为 Widget 的理念,对于 Flutter 来说,手机应用里的所有东西都是Widget,通过可组合的空间集合、丰富的动画库以及分层课扩展的架构实现了富有感染力的灵活界面设计;
• 借助可移植的 GPU 加速的渲染引擎以及高性能本地代码运行时以达到跨平台设备的高质量用户体验。简单来说就是:最终结果就是利用 Flutter 构建的应用在运行效率上会和原生应用差不多。
缺点
• 不支持热更新;
• 三方库有限,需要自己造轮子;
• Dart 语言编写,增加了学习难度,并且学习了 Dart 之后无其他用处,相比 JS 和 Java 来说。
2.介绍下 Flutter 的理念架构
由上图可知,
Flutter框架自下而上分为 Embedder、 Engine和 Framework三层。
Embedder是操作系统适配层,实现了渲染 Surface设置,线程设置,以及平台插件等平台相关特性的适配;
Engine层负责图形绘制、文字排版和提供 Dart运行时,Engine层具有独立虚拟机,正是由于它的存在,Flutter程序才能运行在不同的平台上,实现跨平台运行;
Framework层则是使用 Dart 编写的一套基础视图库,包含了动画、图形绘制和手势识别等功能,是使用频率最高的一层。
3. Flutter 的 FrameWork 层和 Engine 层,作用
Flutter的FrameWork层是用Drat编写的框架(SDK),它实现了一套基础库,包含Material(Android风格UI)和 Cupertino(iOS风格)的UI界面,下面是通用的Widgets(组件),之后是一些动画、绘制、渲染、手势库等。这个纯 Dart实现的 SDK被封装为了一个叫作 dart:ui的 Dart库。我们在使用 Flutter写 App的时候,直接导入这个库即可使用组件等功能。
Flutter的Engine层是Skia 2D的绘图引擎库,其前身是一个向量绘图软件,Chrome和 Android均采用 Skia作为绘图引擎。Skia提供了非常友好的 API,并且在图形转换、文字渲染、位图渲染方面都提供了友好、高效的表现。Skia是跨平台的,所以可以被嵌入到 Flutter的 iOS SDK中,而不用去研究 iOS闭源的 Core Graphics / Core Animation。Android自带了 Skia,所以 Flutter Android SDK要比 iOS SDK小很多。
4. Widget、State、Context 概念
• Widget :在 Flutter 中,几乎所有东西都是 Widget 。将一个 Widget 想象为一个可视化的组件(或与应用可视化方面交互的组件),当你需要构建与布局直接或间接相关的任何内容时,你正在使用 Widget 。
• Widget树: Widget 以树结构进行组织。包含其他 Widget 的 widget 被称为父 Widget( 或 widget 容器) 。包含在父 widget 中的 widget 被称为子Widget 。
• Context :仅仅是已创建的所有 Widget 树结构中的某个 Widget 的位置引用。简而言之,将 context 作为 widget 树的一部分,其中 context 所对应的 widget被添加到此树中。一个 context 只从属于一个 widget ,它和 widget 一样是链接在一起的,并且会形成一个 context 树。
• State :定义了 StatefulWidget 实例的行为,它包含了用于 “交互/干预“ Widget 信息的行为和布局。应用于 State 的任何更改都会强制重建Widget
5. Widget 的 StatelessWidget 和 StatefulWidget 两种状态组件类
• StatelessWidget : 一旦创建就不关心任何变化,在下次构建之前都不会改变。它们除了依赖于自身的配置信息(在父节点构建时提供)外不再依赖于任何其他信息。比如典型的 Text 、 Row 、 Column 、 Container 等,都是 StatelessWidget 。它的生命周期相当简单:初始化、通过 build() 渲染。
• StatefulWidget : 在生命周期内,该类 Widget 所持有的数据可能会发生变化,这样的数据被称为 State ,这些拥有动态内部数据的 Widget 被称为 StatefulWidget 。比如复选框、 Button 等。 State 会与 Context 相关联,并且此关联是永久性的, State 对象将永远不会改变其 Context ,即使可以在树结构周围移动,也仍将与该 context 相关联。当 state 与 context 关联时, state 被视为已挂载。 StatefulWidget 由两部分组成,在初始化时必须要在 createState() 时初始化一个与之相关 的 State 对象。
6.StatefulWidget 的生命周期
Flutter 的 Widget 分为 StatelessWidget 和 StatefulWidget 两种。
其中,StatelessWidget 是无状态的,StatefulWidget 是有状态的,因此实际使用时,更多的是StatefulWidget。
StatefulWidget的生命周期如下图。
• initState(): Widget 初始化当前 State ,在当前方法中是不能获取到 Context 的,如想获取,可以试试 Future.delayed()
• **didChangeDependencies()**:在 initState() 后调用, State 对象依赖关系发生变化的时候也会调用。
• **deactivate()**:当 State 被暂时从视图树中移除时会调用这个方法,页面切换时也会调用该方法,和 Android 里的 onPause 差不多。
• dispose(): Widget 销毁时调用。
• didUpdateWidget(): Widget 状态发生变化的时候调用。
7. Widgets、RenderObjects 和 Elements 的关系
首先看一下这几个对象的含义及作用。
• Widget :仅用于存储渲染所需要的信息。
• RenderObject :负责管理布局、绘制等操作。
• Element :才是这颗巨大的控件树上的实体。
Widget 会被 inflate(填充)到 Element,并由 Element 管理底层渲染树。Widget 并不会直接管理状态及渲染, 而是通过 State 这个对象来管理状态。Flutter 创建 Element 的可见树,相对于 Widget 来说,是可变的,通常界面开发中,我们不用直接操作 Element, 而是由框架层实现内部逻辑。就如一个UI视图树中,可能包含有多个 TextWidget (Widget被使用多次),但是放在内部视图树的视角,这些 TextWidget 都是填充到一个个独立的 Element 中。Element 会持有renderObject 和 widget 的实例。记住,Widget 只是一个配置,RenderObject 负责管理布局、绘制等操作。
在第一次创建 Widget 的时候,会对应创建一个 Element, 然后将该元素插入树中。如果之后 Widget 发生了变化,则将其与旧的 Widget 进行比较,并且相应地更新 Element。重要的是,Element 不会被重建,只是更新而已。
8.状态管理,状态管理框架
Flutter 中的状态和前端 React 中的状态概念是一致的。React 框架的核心思想是组件化,应用由组件搭建而成,组件最重要的概念就是状态,状态是一个组件的 UI 数据模型,是组件渲染时的数据依据。
Flutter 的状态可以分为全局状态和局部状态两种。常用的状态管理有 ScopedModel、BLoC、Redux / FishRedux和Provider。详细使用情况和差异可以自行了解。
9. Flutter 的绘制流程
Flutter 的绘制流程如下图所示:
Flutter 只关心向 GPU 提供视图数据,GPU 的 VSync信号同步到 UI线程,UI线程使用 Dart 来构建抽象的视图结构,这份数据结构在 GPU线程进行图层合成,视图数据提供给 Skia引擎渲染为 GPU 数据,这些数据通过 OpenGL 或者 Vulkan提供给 GPU。
10. Flutter 的线程管理模型
默认情况下,Flutter Engine层会创建一个Isolate,并且Dart代码默认就运行在这个主Isolate上。必要时可以使用spawnUri和spawn两种方式来创建新的Isolate,在Flutter中,新创建的Isolate由Flutter进行统一的管理。
事实上,Flutter Engine自己不创建和管理线程,Flutter Engine线程的创建和管理是 Embeder 负责的,Embeder 指的是将引擎移植到平台的中间层代码。
Flutter Engine 层的架构示意图如下图所示:
在 Flutter 的架构中,Embeder 提供四个Task Runner,分别是 Platform Task Runner、UI Task Runner Thread、GPU Task Runner和IO Task Runner,每个Task Runner负责不同的任务,Flutter Engine 不在乎 Task Runner 运行在哪个线程,但是它需要线程在整个生命周期里面保持稳定。
11.Flutter与原生 Android 、 iOS 进行通信方式
Flutter 通过 PlatformChannel 与原生进行交互,其中 PlatformChannel 分为三种:
BasicMessageChannel :用于传递字符串和半结构化的信息。
MethodChannel :用于传递方法调用(method invocation)。
EventChannel : 用于数据流(event streams)的通信。
同时 Platform Channel 并非是线程安全的 ,更多详细可查阅闲鱼技术的 《深入理解Flutter Platform Channel》
12. Flutter 的热重载
Flutter 的热重载是基于 JIT 编译模式的代码增量同步。由于 JIT 属于动态编译,能够将 Dart 代码编译成生成中间代码,让 Dart VM 在运行时解释执行,因此可以通过动态更新中间代码实现增量同步。
热重载的流程可以分为 5 步,包括:扫描工程改动、增量编译、推送更新、代码合并、Widget 重建。
Flutter 在接收到代码变更后,并不会让 App 重新启动执行,而只会触发 Widget 树的重新绘制,因此可以保持改动前的状态,大大缩短了从代码修改到看到修改产生的变化之间所需要的时间。
另一方面,由于涉及到状态的保存与恢复,涉及状态兼容与状态初始化的场景,热重载是无法支持的,如改动前后 Widget 状态无法兼容、全局变量与静态属性的更改、main 方法里的更改、initState 方法里的更改、枚举和泛型的更改等。
可以发现,热重载提高了调试 UI 的效率,非常适合写界面样式这样需要反复查看修改效果的场景。但由于其状态保存的机制所限,热重载本身也有一些无法支持的边界。