Flutter 简介
Flutter 是一款移动应用程序 SDK,一份代码可以同时生成 iOS 和 Android 两个高性能、高保真的应用程序。Flutter 目标是使开发人员能够交付在不同平台上都感觉自然流畅的高性能应用程序。我们兼容滚动行为、排版、图标等方面的差异。在全世界,Flutter 正在被越来越多的开发者和组织使用,并且 Flutter 是完全免费、开源的。
Flutter 历史
说到 Flutter,可能很多小伙伴都会以为它是新兴的的移动开发框架,其实不然,Flutter 的历史最早可以追溯到 2014 年 10 月,其前身是 Google 内部孵化的 Sky 项目。
不过,随着 Flutter 热度的上升,特别是 2018 年 Flutter 陆续发布了 Beta 版和 Flutter1.0,给很多小伙伴造成了一个误区:认为 Flutter 是最近新兴的一个开发框架。最近,Google 又发布了 1.2 正式版,并且官方也发布了今年的开发路线(参考 Flutter 2019 产品路线图),可以预见,Flutter 将在 2019 年迎来真正的爆发和成长。为了方便读者对 Flutter 有一个更深的了解,下面来看一下 Fluter 的历史:
2014.10 – Flutter 的前身 Sky 在 GitHub 上开源;
2015.10 – 经过一年的开源,Sky 正式改名为 Flutter;
2017.5 – Google I/ O 正式向外界公布了 Flutter,这个时候 Flutter 才正式进去大家的视野;
2018.6 – 距 5 月 Google I/O 1 个月的时间,Flutter1.0 预览版;
2018.12 – Flutter1.0 发布,它的发布将大家对 Flutter 的学习和研究推到了一个新的起点;
2019.2 – Flutter1.2 发布主要增加对 web 的支持。
通过 Flutter 的历史,可以看出 Flutter 正在逐渐的走向成熟和壮大,它的生态圈也在不断的发展,所以现在学习 Flutter 是一个非常的好时机。
Flutter 原理
相比 React Native 和 Weex,Flutter 实现跨平台采用了更为彻底的方案(参考移动跨平台技术方案总结)。它既没有采用 WebView 也没有采用 JavaScript,而是自己实现了一台 UI 框架,然后直接系统更底层渲染系统上画 UI。所以它采用的开发语言不是 JS,而 Dart(Dart 是面向对象的、类定义的、单继承的语言。它的语法类似 C 语言,可以转译为 JavaScript,支持接口 (interfaces)、混入(mixins)、抽象类(abstract classes)、具体化泛型(reified generics)、可选类型(optional typing) 和 sound type syste)。据称 Dart 语言可以编译成原生代码,直接跟原生通信,其原理模型图如下:
同时,Flutter 将 UI 组件和渲染器从平台移动到应用程序中,这使得它们可以自定义和可扩展。Flutter 唯一要求系统提供的是 canvas,以便定制的 UI 组件可以出现在设备的屏幕上,以及访问事件(触摸,定时器等)和服务(位置、相机等)。这是 Flutter 可以做到跨平台而且高效的关键。另外 Flutter 学习了 RN 的 UI 编程方式,引入了状态机,更新 UI 时只更新最小改变区域。
系统的 UI 框架可以取代,但是系统提供的一些服务是无法取代的。Flutter 在跟系统 service 通信方式,采用的是一种类似插件式的方式,或者有点像远程过程调用 RPC 方式,这种方式据说也要比 RN 的桥接方式高效。关于 RN 和 Flutter 到底谁更优秀,有兴趣的读者可以关注下官方的撕逼大战 React Native 团队怎么看待 Flutter 的。
Flutter 和 React Native 底层框架对比
React-Native、Weex 核心是通过 Javascript 开发,执行时需要 Javascript 解释器,UI 是通过原生控件渲染。Flutter 与用于构建移动应用程序的其它大多数框架不同,因为 Flutter 既不使用 WebView,也不使用操作系统的原生控件。相反,Flutter 使用自己的高性能渲染引擎来绘 制 widget。Flutter 使用 C、C ++、Dart 和 Skia(2D 渲染引擎)构建。
Skia 是一个 2D 的绘图引擎库,其前身是一个向量绘图软件,Chrome 和 Android 均采用 Skia 作为绘图引擎。Android 自带了 Skia,所以 Flutter Android SDK 要比 iOS SDK 小很多。
在 ReactNative 中,引入了虚拟 DOM 来减少 DOM 的回流和重绘,系统将虚拟 DOM 与真正的 DOM 进行比较,生成一组最小的更改,然后执行这些更改,以更新真正的 DOM。最后,平台重新绘制真实的 DOM 到画布中。
React Native 是移动开发的一大进步,并且是 Flutter 的灵感来源,但 Flutter 更进一步。在 Flutter 中,UI 组件和渲染器已经从平台中集成到用户的应用程序中。没有系统 UI 组件可以操作,所以原来虚拟控件树的地方现在是真实的控件树,Flutter 渲染 UI 控件树并将其绘制到平台画布上。
如果说非要比较 Flutter 和 React Native 的优势,可以参考下面几点:
UI 一致性
Flutter 因为是自己做的渲染,因此在 iOS 和 Android 的效果基本完全一致。React Native 存在将 RN 控件转换为对应平台原生控件的过程,存在一定的差异(如之前在调研里提到过的 Button 在 iOS 和 Android 下面显示效果不一样)。
动态化技术
Flutter 使用的 Dart 语言,支持 AOT 和 JIT 两种模式,在 Dev 时候,通过 JIT 可以实现热重载,开发者可以即时的看到代码修改的效果。而在 Release Build 的时候,通过 AOT 事先编译,来最大化的优化性能。因此目前 Flutter 不支持代码的热更新,不过在 Flutter 2019 产品路线图)可以看到这方面的消息。
ReactNative 的代码通过加载 JSBundle.js 执行,JSBundle.js 可以保存在本地,也可以通过远程加载。目前有很多 RN 的热更新方案供选择。
App 体积
Flutter iOS 空项目 30M 左右,Android 空项目 7M 左右。(iOS 需要额外集成 Skia)React Native iOS 空项目 3M 左右,Android20M 左右。(Android 会加入 OKHttp 导致体积增大)
Flutter 部分的底层功能在 Android 系统上已经有实现,因此 Android 上适配要好(RN 在 Android 上有可能遇到兼容性问题)。
Flutter 的优势
运行效率上,Flutter 和 ReactNative 都可以达到理论上的 60 帧的刷新率,来实现「Native 般的流畅体验」,Flutter 是全 Native 在执行,基于底层代码(Android 上为 C++ with NDK,iOS 上为 C++ with LLVM),而 ReactNative 是 Native 控件 + JavaScript 代码,实际性能上,Flutter 应该优于 ReactNative,据官方文档,Flutter 可以在支持的设备上达到 120FPS,而 ReactNative 的文档上,只提到了可以达到 60FPS。
兼容性上,Flutter 提供的 widget 都是基于 skia 来实现和精心定制的,与具体平台没关,所以能保持很高的跨 os 跨 os version 的兼容性。Flutter 从更基础的层去抹平平台差异,站在了更宽广、更可控的一个基础平台上去演变和发展。Flutter 官方提供了大部分 Material Design 控件的实现(甚至比 Android Design Support 实现的更多)。
Flutter 开发语言 Dart
为什么要使用 Dart 语言
学习 Flutter 就不得不提到 Dart,那 Flutter 和 Dart 有什么关系?确实有关系,早期的 Flutter 团队评估了十多种语言,并选择了 Dart,因为它符合他们构建用户界面的方式,读者可以去八卦下为什么要使用 Dart 语言的推文。
Dart 能成为 Flutter 不可或缺的一部分,根本原因还是因为其具有以下特性:
1)Dart 是 AOT(Ahead Of Time)编译的,编译成快速、可预测的本地代码,使 Flutter 几乎都可以使用 Dart 编写。这不仅使 Flutter 变得更快,而且几乎所有的东西(包括所有的小部件)都可以定制;
2)Dart 也可以 JIT(Just In Time)编译,开发周期异常快,工作流颠覆常规(包括 Flutter 流行的亚秒级有状态热重载);
3)Dart 可以更轻松地创建以 60fps 运行的流畅动画和转场。Dart 可以在没有锁的情况下进行对象分配和垃圾回收。就像 JavaScript 一样,Dart 避免了抢占式调度和共享内存(因而也不需要锁)。由于 Flutter 应用程序被编译为本地代码,因此它们不需要在领域之间建立缓慢的桥梁(例如,JavaScript 到本地代码)。它的启动速度也快得多;
4)Dart 使 Flutter 不需要单独的声明式布局语言,如 JSX 或 XML,或单独的可视化界面构建器,因为 Dart 的声明式编程布局易于阅读和可视化。所有的布局使用一种语言,聚集在一处,Flutter 很容易提供高级工具,使布局更简单;
5)开发人员发现 Dart 特别容易学习,因为它具有静态和动态语言用户都熟悉的特性。
编译与执行
历史上,计算机语言分为两组:静态语言(例如,Fortran 和 C,其中变量类型是在编译时静态指定的)和动态语言(例如,Smalltalk 和 JavaScript,其中变量的类型可以在运行时改变)。静态语言通常编译成目标机器的本地机器代码(或汇编代码)程序,该程序在运行时直接由硬件执行。动态语言由解释器执行,不产生机器语言代码。
当然,事情后来变得复杂得多。虚拟机(VM)的概念开始流行,它其实只是一个高级的解释器,用软件模拟硬件设备。虚拟机使语言移植到新的硬件平台更容易。因此,VM 的输入语言常常是中间语言。例如,一种编程语言(如 Java)被编译成中间语言(字节码),然后在 VM(JVM)中执行。
另外,现在有即时(JIT)编译器。JIT 编译器在程序执行期间运行,即时编译代码。原先在程序创建期间(运行时之前)执行的编译器现在称为 AOT 编译器。
一般来说,只有静态语言才适合 AOT 编译为本地机器代码,因为机器语言通常需要知道数据的类型,而动态语言中的类型事先并不确定。因此,动态语言通常被解释或 JIT 编译。
在开发过程中 AOT 编译,开发周期(从更改程序到能够执行程序以查看更改结果的时间)总是很慢。但是 AOT 编译产生的程序可以更可预测地执行,并且运行时不需要停下来分析和编译。AOT 编译的程序也更快地开始执行(因为它们已经被编译)。
相反,JIT 编译提供了更快的开发周期,但可能导致执行速度较慢或时快时慢。特别是,JIT 编译器启动较慢,因为当程序开始运行时,JIT 编译器必须在代码执行之前进行分析和编译。研究表明,如果开始执行需要超过几秒钟,许多人将放弃应用。
Dart 的编译与执行
在创造 Dart 之前,Dart 团队成员在高级编译器和虚拟机上做了开创性的工作,包括动态语言(如 JavaScript 的 V8 引擎和 Smalltalk 的 Strongtalk)以及静态语言(如用于 Java 的 Hotspot 编译器)。他们利用这些经验使 Dart 在编译和执行方面非常灵活。
Dart 是同时非常适合 AOT 编译和 JIT 编译的少数语言之一(也许是唯一的“主流”语言)。支持这两种编译方式为 Dart 和(特别是)Flutter 提供了显著的优势。
JIT 编译在开发过程中使用,编译器速度特别快。然后,当一个应用程序准备发布时,它被 AOT 编译。因此,借助先进的工具和编译器,Dart 具有两全其美的优势:极快的开发周期、快速的执行速度和极短启动时间。
Dart 在编译和执行方面的灵活性并不止于此。例如,Dart 可以编译成 JavaScript,所以浏览器可以执行。这允许在移动应用和网络应用之间重复使用代码。开发人员报告他们的移动和网络应用程序之间的代码重用率高达 70%。通过将 Dart 编译为本地代码,或者编译为 JavaScript 并将其与 node.js 一起使用,Dart 也可以在服务器上使用。
最后,Dart 还提供了一个独立的虚拟机(本质上就像解释器一样),虚拟机使用 Dart 语言本身作为其中间语言。
Dart 可以进行高效的 AOT 编译或 JIT 编译、解释或转译成其他语言。Dart 编译和执行不仅非常灵活,而且速度特别快。
AOT 编译和“桥”
前面讨论过一个有助于保持顺畅的特性,那就是 Dart 能 AOT 编译为本地机器码。预编译的 AOT 代码比 JIT 更具可预测性,因为在运行时不需要暂停执行 JIT 分析或编译。
然而,AOT 编译代码还有一个更大的优势,那就是避免了“JavaScript 桥梁”。当动态语言(如 JavaScript)需要与平台上的本地代码互操作时,它们必须通过桥进行通信,这会导致上下文切换,从而必须保存特别多的状态(可能会存储到辅助存储)。这些上下文切换具有双重打击,因为它们不仅会减慢速度,还会导致严重的卡顿。
说明:即使编译后的代码也可能需要一个接口来与平台代码进行交互,并且这也可以称为桥,但它通常比动态语言所需的桥快几个数量级。另外,由于 Dart 允许将小部件等内容移至应用程序中,因此减少了桥接的需求。
布局
Dart 的另一个好处是,Flutter 不会从程序中拆分出额外的模板或布局语言,如 JSX 或 XML,也不需要单独的可视布局工具。以下是一个简单的 Flutter 视图,用 Dart 编写:
new Center(child:
new Column(children: [
new Text(‘Hello, World!’),
new Icon(Icons.star, color: Colors.green),
])
)
并且随着 Dart 2 的发布,上面的代码也变得越来越可读,因为 new 和 const 关键字变得可选,所以静态布局看起来像是用声明式布局语言编写的:
Center(child:
Column(children: [
Text(‘Hello, World!’),
Icon(Icons.star, color: Colors.green),
])
)
至于,困扰原生开发人员的一个问题是:为什么缺乏专门的布局语言怎么会被称为优势呢?原生开发人员可以在下面的文章中找到答案:“为什么原生应用程序开发人员应认真看待 Flutter”
学习路线
学习任何一门技术,最主要的渠道就是官方资料,由于是 Google 的产品,因此从一开始就受到很多开发者的喜爱,因此其社区建设也相对较快,读者可以现场 Flutter 中文社区了解一些 Flutter 开发的基础,然后再结合一些开源项目进行学习。
Fluuter 网上的学习资料也很多,可以参考下面的链接进行深入的学习:Flutter 学习线路