面试官: 你有用过Flutter吗? Flutter架构是怎么样,为什么会比其他如ReactNative好
心理分析: 面试官所在的项目是用Flutter 开发的,或者正在转Flutter这种技术。如果你不会,面试官在第一眼会认为后面来应聘的人或许会、为了杜绝这种情况发生,你一定要说以前研究过,写过相关代码。其实”有用过Flutter“对大多数求职只是 会与不会 的问题。这不是面试官 想了解的。面试官的重点 你了解的有多深。
接下来,会问你他的原理 你是怎么看。 它的优缺点。为什么比其他的好。从原理层来解析。这才是最难的。
这篇文章 从原理层说明他们的区别
求职者: 应该做好被虐的准备。如果看了这篇文章,应付下来基本没有多大的问题
在Flutter诞生之前,已经有许多跨平台UI框架的方案,比如基于WebView的Cordova、AppCan等,还有使用HTML+JavaScript渲染成原生控件的React Native、Weex等。
框架架构
反观Flutter
首先看一下Flutter的架构图
Flutter三层架构
Framework
Engine
Embedder
1.Framework使用dart实现,包括Material Design风格的Widget,Cupertino(针对iOS)风格的Widgets,文本/图片/按钮等基础Widgets,渲染,动画,手势等。此部分的核心代码是:flutter仓库下的flutter package,以及sky_engine仓库下的io,async,ui(dart:ui库提供了Flutter框架和引擎之间的接口)等package。
2.Engine使用C++实现,主要包括:Skia,Dart和Text。Skia是开源的二维图形库,提供了适用于多种软硬件平台的通用API
3.Embedder是一个嵌入层,即把Flutter嵌入到各个平台上去,这里做的主要工作包括渲染Surface设置,线程设置,以及插件等。从这里可以看出,Flutter的平台相关层很低,平台(如iOS)只是提供一个画布,剩余的所有渲染相关的逻辑都在Flutter内部,这就使得它具有了很好的跨端一致性。
从架构图可以看出,从头到尾重写一套跨平台的UI框架,包括UI控件、渲染逻辑甚至开发语言。渲染引擎依靠跨平台的Skia图形库来实现,依赖系统的只有图形绘制相关的接口,可以在最大程度上保证不同平台、不同设备的体验一致性,逻辑处理使用支持AOT的Dart语言,执行效率也比JavaScript高得多
万物皆widget
目前上主流的思想,都希望将各个ui控件接耦,慢慢演变出组件化的思想。
Flutter控件主要分为两大类,
- StatelessWidget
- StatefulWidget,
StatelessWidget
用来展示静态的文本或者图片,如果控件需要根据外部数据或者用户操作来改变的话,就需要使用
StatefulWidget
。
State的概念也是来源于Facebook的流行Web框架React,React风格的框架中使用控件树和各自的状态来构建界面,当某个控件的状态发生变化时由框架负责对比前后状态差异并且采取最小代价来更新渲染结果。
绘图基本原理
从下图这里可以看出 Flutter和 React-Native之众的本质区别:React-Native之类只是扩展调用 OEM组件,而 Flutter是自己渲染。
在 Flutter Architecture的解释中,Google还提供了一张更为详尽的图来解释 Flutter的原理:
这张图解释得更清晰一些:Flutter只关心向 GPU提供视图数据,GPU的 VSync信号同步到 UI线程,UI线程使用 Dart来构建抽象的视图结构,这份数据结构在 GPU线程进行图层合成,视图数据提供给 Skia引擎渲染为 GPU数据,这些数据通过 OpenGL或者 Vulkan提供给 GPU。
所以 Flutter并不关心显示器、视频控制器以及 GPU具体工作,它只关心 GPU发出的 VSync信号,尽可能快地在两个 VSync信号之间计算并合成视图数据,并且把数据提供给 GPU。
Flutter面试会出现的几个问题
了解 Flutter的基本概念后,自然有几个疑问亟待解决。
为什么使用 Dart?能说说其中的原因吗
- Dart 的性能更好。Dart在 JIT模式下,速度与 JavaScript基本持平。但是 Dart支持 AOT,当以 AOT模式运行时,JavaScript便远远追不上了。速度的提升对高帧率下的视图数据计算很有帮助。
- Native Binding。在 Android上,v8的 Native Binding可以很好地实现,但是 iOS上的 JavaScriptCore不可以,所以如果使用 JavaScript,Flutter 基础框架的代码模式就很难统一了。而 Dart的 Native Binding可以很好地通过 Dart Lib实现。
- Fuchsia OS,看起来不像原因的一个原因。Fuchsia OS内置的应用浏览器就是使用 Dart语言作为 App的开发语言。而且实际上,Flutter是 Fuchisa OS的应用框架概念上的一个子集。(Flutter源代码和编译工具链也充斥了大量的 Fuchsia宏)
- Dart是类型安全的语言,拥有完善的包管理和诸多特性。Google召集了如此多个编程语言界的设计专家开发出这样一门语言,旨在取代 JavaScript,所以 Fuchsia OS内置了 Dart。Dart可以作为 embedded lib嵌入应用,而不用只能随着系统升级才能获得更新,这也是优势之一。
Skia是什么? 为什么Flutter会引入Skia
前面提到了 Flutter只关心如何构建视图抽象结构,向 GPU提供视图数据。Skia就是 Flutter向 GPU提供数据的途径。
- Skia是一个 2D的绘图引擎库,其前身是一个向量绘图软件,Chrome和 Android均采用 Skia作为绘图引擎。Skia提供了非常友好的 API,并且在图形转换、文字渲染、位图渲染方面都提供了友好、高效的表现。Skia是跨平台的,所以可以被嵌入到 Flutter的 iOS SDK中,而不用去研究 iOS闭源的 Core Graphics / Core Animation。
- Android自带了 Skia,所以 Flutter Android SDK要比 iOS SDK小很多。
Flutter的架构是怎么样的,你是怎么理解FLutter的架构
我们可能只知道 Flutter做了什么,始终都还没有从侧面观察 Flutter的整个架构设计,了解 Flutter如何去做。
这张图了解过 Flutter的人可能很多地方都看过,这边来详细解释一下:
Flutter Framework: 这是一个纯 Dart实现的 SDK,类似于 React在 JavaScript中的作用。它实现了一套基础库, 用于处理动画、绘图和手势。并且基于绘图封装了一套 UI组件库,然后根据 Material 和Cupertino两种视觉风格区分开来。这个纯 Dart实现的 SDK被封装为了一个叫作 dart:ui的 Dart库。我们在使用 Flutter写 App的时候,直接导入这个库即可使用组件等功能。
Flutter Engine: 这是一个纯 C++实现的 SDK,其中囊括了 Skia引擎、Dart运行时、文字排版引擎等。不过说白了,它就是 Dart的一个运行时,它可以以 JIT、JIT Snapshot 或者 AOT的模式运行 Dart代码。在代码调用 dart:ui库时,提供 dart:ui库中 Native Binding 实现。 不过别忘了,这个运行时还控制着 VSync信号的传递、GPU数据的填充等,并且还负责把客户端的事件传递到运行时中的代码。
在了解屏幕绘图的基本原理和 Flutter的一个整体概念后,我们下面详细地来看一下 Flutter的大概实现。
要理解 Flutter的原理,我们从 entry point开始看 Flutter的代码。由于应用框架大同小异,所以下文提及 Flutter的代码即指代 Flutter Engine的代码,而非 Flutter Dart Framework代码。
下图是我简单整理了一下 Flutter应用启动后的执行顺序
- 在应用的 View Controller 初始化后,会实例化一个 Flutter project的抽象(以下简称 project)。project会初始化一个 platform view的抽象实例,这个抽象实例会负责创建 Flutter 的运行时(以下简称 engine)。
- 当 View Controller将要显示时,调用 project查找和组合 Flutter的应用资源 bundle,并且把资源提供给 engine。
- engine在真正需要执行资源 bundle时才会创建 Dart执行的环境(懒加载,以下简称 Dart Controller),然后设置视图窗口的一些属性等东西(这是绘图引擎必需的)。
- 然后 engine中的 Dart Controller会加载 Dart代码并执行,执行的过程中会调用 dart:ui的 native binding实现向 GPU提供数据。
Flutter SDK体积为什么非常大?这个你知道吗
- Flutter应用的体积由两部分组成:应用代码和 SDK代码。应用代码是 Dart编译后的代码,如果做成可动态下发,那么这部分可以不计。
- SDK代码比较大就有点无奈了,SDK的组成部分有 Dart VM,Dart标准库,libwebp、libpng、libboringssl等第三方库,libskia,Dart UI库,然后再加上 icu_data,可能在单 arch下(iOS),SDK体积达到 40+MB。其中仅仅 Dart VM(不包含标准库)就达到了 7MB。
- Flutter SDK是 dynamic framework,如此大的二进制体积可能会造成动态链接耗时长。而如果静态链接,可能原来比较大的 App很有可能造成 TEXT段超标。
Flutter可以跑多个实例吗?(连环炮)
可以的。
- Dart毫无疑问是一门优秀的编程语言,我也曾尝试将 Dart独立出来作为一个通用的 SDK。
- Dart SDK托管在 chromium项目中,并且提供了 cross building的选项。原版即提供了 Android Build脚本。但在 iOS上原版行不通,猜测主要是标准库的问题。
- 在 Flutter iOS项目中,Dart 标准库提供了一份完全不同的实现。而且,想要把 Dart VM和标准库从 Flutter剥离出来,太困难了。并且单个 arch下,Dart VM加标准库的体积是 > 10MB的。