Flutter开发(介绍篇)

  1. 介绍
  2. 优点缺点
  3. 实现原理
    1. 框架
    2. 编译
    3. 控件
    4. 热重载
  4. 应用场景
    1. App端
    2. Web端
    3. 桌面操作系统
  5. Ref:

介绍

1. Flutter是什么?

Flutter是谷歌的是一款完全免费、开源的移动UI框架,其实也就是移动应用程序SDK,里面包含框架、控件和一些工具,可以快速在iOS和Android上构建相对高质量的原生用户界面,它也可以与现有的代码一起工作。

简单来说,Flutter可以用一套代码同时构建Android和iOS应用,并且可以达到原生应用一样的性能。

2. 使用什么语言?

Dart,它是一门动态类型或者说是可选类型的语言,可选类型的意思是既可以是动态类型也可以是静态类型。
如果你想体验Dart语言,但又不想在你的电脑上安装Dart开发环境,那你可以使用Google提供的Dart云编译服务——DartPad

DartPad是一个自由、开放的源码服务,帮助开发人员学习Dart语言,进入DartPad的源代码会被发送到谷歌云计算平台上运行,服务器会将源代码进行编译并处理成JavaScript返回给浏览器,编译产生的错误和警告也会返回

3. 响应式设计

Flutter采用响应式界面开发,以事件数据流作为核心开发模型。它的界面只能被描述而不能被操作,例如我们常用的TextView.setText()这样的API操作是不存在的。Flutter中使用Widget来描述界面,Widget只是界面的配置信息,当数据改变时,需要重新创建Widget去更新界面,这样虽然Widget会创建销毁得非常频繁,但Flutter使用的Dart虚拟机能高效处理这种短周期的轻量对象。

可能这里有人对响应式编程还不了解,这里简单介绍一下,响应式编程本质是基于观察者模式实现的,它在界面开发上的应用目的与MVVM有类似之处,但因MVVM实现Model和View双向绑定时有一定程度的状态混乱,所以后来结合函数式编程的思想催生了响应式界面开发,将MVVM中的VM使用事件数据流替代了,使Model利用事件流回到最初的事件驱动,再利用数据流来驱动View刷新,这里的事件流可以类比为被监听者,数据流可以类比为监听者预设的回调函数。

(响应式是与异步数据流交互的编程范式,思路是除了点击和悬停(hover)的事件,你还可以给其他任何事物创建数据流。数据流无处不在,任何东西都可以成为一个数据流,例如变量、用户输入、属性、缓存、数据结构等等,你可以监听这样的数据流,并做出相应的反应)

4. 热重载

只需要点击保存或者“Hot Reload”按钮,就可以立即更新到正在运行的设备上,不需要重新编译App,甚至不需要重启App,立即就可以看到更新后的样式。

优点缺点

1. 优点

Flutter 最受欢迎的功能之一是其极速热重载。在开发过程中,Flutter 使用 JIT 编译器,通常可以在一秒之内重新加载并继续执行代码。只要有可能,应用程序状态在重新加载时保留下来,以便应用程序可以从停止的地方继续。

同时Flutter以 60fps 运行,使用 Flutter 创建的用户界面的性能远远优于使用其他跨平台开发框架创建的用户界面,不仅仅比跨平台的应用程序好,而且和最好的原生应用程序一样好。

一般移动应用上导致卡顿严重的原因还有一部分是源于垃圾回收的处理上,事实上,这只是访问共享资源(内存)的一种特殊情况,在很多语言中都需要使用锁,但在回收可用内存时,锁会阻止整个应用程序运行。但是 Dart 几乎可以在没有锁的情况下执行垃圾回收。Dart 使用先进的分代垃圾回收和对象分配方案,该方案对于分配许多短暂的对象(对于 Flutter 这样的反应式用户界面来说非常完美,Flutter 为每帧重建不可变视图树)都特别快速。Dart 可以用一个指针凹凸分配一个对象(不需要锁)。这也会带来流畅的滚动和动画效果,而不会出现卡顿。

另外,在 Flutter 的界面布局上,是直接通过 Dart 编码来定义,不需要使用 XML 或模板语言,也不需要使用可视化设计器之类的工具,是一种可阅读即可视的体验。

Flutter 内置了对Material Design和Cupertino (iOS-flavor)的 UI 组件库,也提供了可定制的 UI 组件,不再受制于OEM控件的限制

2. 缺点

增加了包体,Android版Flutter应用程序(没有Material Components,只是一个Center构建的单个widget的应用)的大小,并将其打包为release版,大小约为6.7MB。
对于这个简单的应用程序,核心引擎大约3.3MB,框架+应用程序代码大约是1.25MB,LICENSE文件(包含在app.flx中)是55k,必需的Java代码.dex为40k,并且约有2.1MB的ICU数据。

3. 与其它跨平台方案对比

Flutter将UI组件和渲染器从平台移动到应用程序中,这使得它们可以自定义和可扩展。Flutter唯一要求系统提供的是canvas(画布,像iOS中的CoreGraph),以便定制的UI组件可以出现在设备的屏幕上。Dart程序和执行数据编码和解码的原生平台代码(蓝色,适用于iOS或Android)之间仍然有一个接口,但这能比JavaScript桥接器快几个数量级。下图是Flutter与React Native的实现对比

实现原理

框架

Flutter的架构主要分成三层:Framework,Engine和Embedder。
Framework使用dart实现,包括Material Design风格的Widget,和针对iOS的Cupertino风格的Widgets,其中又包括文本、图片、按钮等基础Widgets,然后是渲染,动画,手势等组成。

Engine使用C++实现,主要包括:Skia,Dart和Text。
Skia是开源的二维图形库,提供了适用于多种软硬件平台的通用API。其已作为Google Chrome,Chrome OS,Android, Mozilla Firefox, Firefox OS等其他众多产品的图形引擎,Text文本渲染(先经过衍生自minikin的libtxt库,再由HartBuzz选择字型和成型),在Android和Fuchsia上使用FreeType渲染,在iOS上使用CoreGraphics来渲染字体。
Dart部分主要包括:Dart Runtime,Garbage Collection(GC),如果是Debug模式的话,还包括JIT(Just In Time)支持。

Embedder是一个嵌入层,即把Flutter嵌入到各个平台上去。
这里做的主要工作包括渲染Surface设置,线程设置,以及插件等。从这里可以看出,Flutter的平台相关层很低,平台(如iOS)只是提供一个画布,剩余的所有渲染相关的逻辑都在Flutter内部,这就使得它具有了很好的跨端一致性。

编译

编译器
Dart2js:将Dart转换为js后再由浏览器上js的V8引擎进行词法和语法分析,得到语法树后再解释执行。
DartVM,它是使用LLVM实现的,可以实现JIT和AOT两种编译方式。

编译方式:可以分为JIT和AOT

JIT:Script Snapshot AOT:Machine Code

JIT全称是Just In Time,代码可以在程序执行时期编译,因为要在程序执行前进行分析、编译,JIT编译可能会导致程序执行时间较慢;而AOT编译,全称Ahead Of Time,是在程序运行前就已经编译,从开发者修改代码、编译较慢,但运行时不需要进行分析、编译,因此执行速度更快。

在Release模式下:

上图为iOS的编译流程,gen_snapshot是dart编译器,采用了tree shaking(类似依赖树逻辑,可生成最小包,也因而在Flutter中禁止了dart支持的反射特性)等技术,负责生成汇编形式机器代码。再通过xcrun等工具链生成最终的App.framework。所有的dart代码,包括业务代码,三方package代码,它们所依赖的flutter框架代码,最终将会编译成App.framework。

上图为Android编译流程,vm/isolate_snapshot_data/instr内容均为arm指令,其中vm_中涉及runtime等服务(如gc),用于初始化DartVM。isolate__则对应了我们的应用dart代码,用于创建一个新的isolate,isolate/vm_snapshot_data/instr均最后位于app的本地data目录下,而此部分又属于可写内容,可通过下载并替换的方式,完成App的动态更新。

而iOS的Flutter.framework和Android的flutter.jar对应了Flutter架构中的engine部分,以及Embedder。默认是从google仓库拉取。当需要自定义修改的时候,可通过下载engine源码,利用Ninja构建系统来生成。
Flutter相关代码的最终产物是:App.framework(dart代码生成)和Flutter.framework(引擎)。

在Debug模式下:

iOS的Flutter.framework和flutter.jar中是有JIT支持的,在Release模式下并没有JIT部分。
而App.framework只有几个简单的API,其Dart代码存在于snapshot_blob.bin文件里,位于flutter_assets下。这部分的snapshot是脚本快照,里面是简单的标记化的源代码。所有的注释,空白字符都被移除,常量也被规范化,没有机器码,tree shaking或混淆。

小结一下,debug阶段下是使用Kernel Snapshot模式(对应JIT编译),将dart代码生成标记化的源代码,在运行时编译,解释执行;release阶段下,ios使用AOT编译,编译器将dart代码生成汇编代码,最终生成app.framwork,android使用了Core JIT编译,dart转化为二进制模式,在VM启动前载入。

控件

如上图所示,在Flutter界面渲染过程分为三个阶段:布局、绘制、合成,布局和绘制在Flutter框架中完成,合成则交由引擎负责。

Flutter的控件树在实际显示时会转换成对应的渲染对象(RenderObject)树来实现布局和绘制操作。中间的Element是Flutter用来分离控件树和真正的渲染对象的中间层,控件用来描述对应的element属性,控件重建后可能会复用同一个element。

在Flutter里面,一切皆控件,它们的基类都是Widget,通过组合、嵌套不同类型的控件,就可以构建出任意功能、任意复杂度的界面。Widget 是每个 Flutter 应用的基础。每个 Widget 是一部分用户界面上不可变的定义。和其他框架把 View、controller、 Layout 和其他资源分开定义不一样,Flutter 具有一致的、唯一的对象模型Widget。

Flutter控件主要分为两大类,StatelessWidget和StatefulWidget,StatelessWidget用来展示静态的文本或者图片,如果控件需要根据外部数据或者用户操作来改变的话,就需要使用StatefulWidget。StatelessWidget和StatefulWidget并不会直接影响RenderObject的创建,它们只负责创建对应的RenderObjectWidget,StatelessElement和StatefulElement也是类似的功能。

StatelessWidget是无中间状态变化的widget,需要更新展示内容就得通过重新new,flutter推荐尽量使用StatelessWidget。

StatefullWidget是存在中间状态变化,它通过引入state类存放中间态,通过调用state.setState()进行此节点及以下的整个子树更新。这也是反映flutter界面开发是一种响应式编程,数据变更时发送通知到对应的可变更节点,由上到下重新create widget树进行刷新,这种思路比较简单,不用关心数据变更会影响到哪些节点。

热重载

Flutter通过将新的代码注入到正在运行的DartVM中,来实现Hot Reload这种神奇的效果,在DartVM将程序中的类结构更新完成后,Flutter会立即重建整个控件树,从而更新界面。流程如下图所示

应用场景

App端

1. App开发

2. 包开发

包(Package),其实就是为Flutter的App或者Module而提供的,换其它的一些说法也可以说是组件、工具库包等。要和原生交互的,比如获取设备信息或者启动设备硬件等原生操作,又或者要封装Flutter框架中的功能,都可以通过这个形式实现后交付给Flutter项目或模块里使用。但是由原生触发Flutter中的函数或向Flutter索取信息的就没必要使用Flutter的插件实现了,也不合适,因为实现出来的Package并不是提供给Flutter项目使用,不过这种情况相对较少。原生和Flutter的交互主要是通过两端均注册对应的channel,然后利用其进行通信。

3. 混合开发

混合开发就是在原有的原生工程上创建Flutter的模块(Module),使原项目可以逐渐过渡,而不用一次性全部转移到Flutter上实现。

Web端

Flutter团队正在开发一个Web的架构,叫作Hummingbird。与在移动应用中渲染出控件及界面一样,Hummingbird其实是在引擎层面上,即dart:ui代替原来的引擎,从而实现了在Web端上的渲染。

Flutter的团队也正在选择一组Web技术来完成构建的任务。Flutter一次渲染一帧UI,在每个帧内Flutter会构建widgets,执行布局,最后在屏幕上绘制。构建是通过dart2js,将所有 widgetwidget frame都编译成了JavaScript,移植到Web上。布局则是除了文本外其它内容都由框架进行布局并编译到Web。而最难的部分则是绘制,目前在探索的绘制方案主要包括:HTML+CSS+CanvasCSS Paint API两种。

CSS Paint API,它不是JavaScript独立完成的,它有自己的内存空间,它会在 DOM 更改提交之后,在浏览器的绘制阶段执行绘制工作,然后它是由显示列表所支持而非位图,这样它就具有了2D canvas的绘制效率和无像素化的优点,但不支持绘制文本。

HTML + CSS的方式可以称作为一种叫DomCanvas的画布,它被浏览器的显示列表支持,浏览器的渲染引擎可以帮我们完成一些优化,还可以任意应用变换而不必担心像素化。但若无法使用DomCanvas来表现图片的话,则转为使用Canvas,其性能和绘图兼容性好,但其表示为位图导致存在像素化的问题,及其操作代价较大,还需继续探研高效的方法。

桌面操作系统

Flutter 的设计理念就是希望它可以作为一个灵活且便携的 UI 工具包,以适应各种需要绘制屏幕内容的平台。根据一些新闻消息,像Flutter Desktop Embedding 就是一个使 Flutter 运行于 macOS、Linux 和 Windows 等桌面操作系统的项目。

Ref:

不要一知半解,深入理解flutter的编译原理

深入了解Flutter界面开发

揭秘Flutter Hot Reload

Dart语法学习

为什么 Flutter 会选择 Dart

Hummingbird: 在Web上运行Flutter应用

Flutter原理与实践

Flutter中文网


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 mingfungliu@gmail.com

文章标题:Flutter开发(介绍篇)

文章字数:3.9k

本文作者:Mingfung

发布时间:2019-01-09, 21:24:00

最后更新:2019-10-11, 08:52:23

原始链接:http://blog.ifungfay.com/前端/Flutter开发(介绍篇)/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏

宝贝回家