跨平台技术Flutter的将来

  1、移动跨平台技术演进前端

  1. 引言java

  移动互联网发展十余年,伴随着 Android、iOS 等智能手机的不断普及,移动端已逐步取代 PC 端,成为兵家必争之地。正所谓“得移动端者得天下”,移动端已成为互联网领域最大的流量分发入口,一大批互联网公司正是在这大趋势下崛起。web

  2. 为何须要跨平台技术算法

  伴随着移动互联网的高速发展,公司间竞争愈来愈激烈,如何将好想法快速落地、快速试错,成为备受关注的问题。提高研发效率、缩短研发周期,保障产品快速试错并能快速迭代新功能,让新产品新功能以最快的速度同时抵达 Android、iOS 等多端用户。shell

  众所周知,Android 应用采用 Java 或 Kotlin 编写,iOS 应用采用 Objective-C 或 Swift 编写,Web 端采用 HTML /CSS/JavaScript 编写。当须要开发支持多端的应用,每一端都须要独立研发、测试,一直到上线,以及后续的维护工做,工做量成倍增涨,势必延长研发周期。编程

  为了解决多端独立开发的问题,跨平台技术便应运而生,各大互联网公司为此都投入大量人力,因而出现了各类跨平台技术框架,面对移动领域的跨平台技术方案的层出不穷,又该如何作技术选型呢?小程序

  3. 移动端技术选型架构

  做为移动端的跨端技术方案,所关注无外乎如下这4个方面:研发效率、动态性、多端一致性、性能体验。
1.jpg
  研发效率:最大化代码复用,减小多端差别的适配工做量,下降开发成本,专一业务开发,实现“write once,run everywhere”的终极目标。效率提高是贯穿整个业务的生命周期线,即使业务上线后,可持续下降后续的维护成本,加快新feature的迭代速度,这是一个持续的效率收益。固然,这里不得不说,任何一门新技术在开发启动学习阶段会有一些成本,但上手后的收益是长期的。并发

  动态化:突破渠道的更新频率,可快速迭代新功能,这一点不只是跨平台技术的诉求,也是Native技术必备的杀手锏,这也是评估跨端技术的一个重要考核点。app

  多端一致性:好产品在多端UI设计上,每每是总体风格统一,因此业务方采用原生各自独立开发完成后,还需额外花很多时间来修改UI以保证多端一致性;可见,各端独立实现开发方式,带来的效率滞后,不只仅是Android和iOS各开发一份代码的工做量,还有双端UI的一致性对齐的工做。

  性能体验:通常地,跨端技术方案拥有以上多重优点,但在性能方面比原生流畅更差些。牺牲部分体验换来效率提高,这一点也是情理之中,试想一下,跨平台技术方案同时兼得这4点,那么原生技术恐怕已退出历史舞台,早已经是跨平台技术的天下,因此每每跨平台技术的性能优劣便成为核心指标。

  4. 跨平台技术划分

  对研发效率和体验的不断追逐,移动端的跨平台技术方框架层出不穷,然则天下武功众多,万变不离其宗,从其核心本质来划分,可大体分为如下三大类:
2.jpg
  Web技术:主要依赖于WebView的技术,功能支持受限,性能体验不好,好比PhoneGap、Cordova、小程序。

  原生渲染:使用JavaScript做为编程语言,经过中间层转化为原生控件来渲染UI界面,好比React Native、Weex。

  自渲染技术:自行实现一套渲染框架,可经过调用skia等方式完成自渲染,而不依赖于原生控件,好比Flutter、Unity。

  5. 跨平台技术演进

  跨平台技术,一直以来是每个有追求的开发者所追逐的梦想,同时也是守旧者的噩梦,跨平台的多端一体化方案势必颠覆现有的原生各端独立开发模式,接下来列举众多的跨平台技术中最为关键的几个技术方案的演进阶段。
3.jpg
  从上图能够看出,技术演进过程大体分如下三个阶段:

  第一阶段,采用WebView技术绘制界面的Hybrid混合开发技术,经过JS Bridge 将系统部分能力暴露给 JS 调用,其缺点是性能较差,功能受限,扩展性差,不适合交互复杂的场景,好比Cordova。

  第二阶段,针对WebView界面性能等问题,因而绘制交还原生渲染,仅仅经过JS调用原生控件,相比WebView技术性能体验更好,这是目前绝大部分跨平台框架的设计思路,好比React Native、Weex。另外,最近小程序也比较火,第一和第二阶段的融合,依然采用WebView做为渲染容器,经过限制Web技术栈的子集,规范化组件使用,并逐步引入原生控件表明WebView渲染,以提高性能。

  第三阶段,虽然经过桥接技术使用原生控件解决了功能受限问题,提高性能体验,但相比原生体验差距仍是比较大,以及处理平台差别性很是耗费人力。因而Flutter提出自带渲染引擎的解决方案,尽量减小不一样平台间的差别性, 同时媲美原生的高性能体验,所以业界对 Flutter有着极高的关注度。

  面对现有的如此多跨平台方案,为什么当下最火的跨平台技术是Flutter,有哪些优点呢?

  RN、Weex均使用JavaScript做为编程语言,JavaScript做为前端开发语言,在跨平台开发中可谓大放异彩,利用web技术不只能开发出网站,也能够开发手机端web应用和移动端应用程序,似有一统三界(Android、iOS、Web)的趋势,这就是你们常说的“大前端”时代。这些技术方案流畅度不太好,平台一致性较差,至今还没能大面积取代原生开发。

  Flutter是以Dart语言编写,开发体验更接近客户端,从你们使用反馈来看也是如此,Flutter开发环境这一套的流程对于前端开发来讲并不太友好。Flutter的定位一样是多端一体化,可是以客户端为首,先磨平Android和iOS双端开发体验,再逐步向Web端渗透,从Flutter规划的Roadmap也能看出,Flutter for web目前仍处于预览版,Flutter客户端方向都已经如火如荼上线了很多应用。

  在此以前,你们常说“大前端”,对于Flutter技术,在笔者看来称之为“大移动端”更贴切,Flutter的UI框架优先支持客户端(Android/iOS)应用的同时,而后再适配Web端。移动互联网时代,很多公司都制定“移动优先”的战略,甚至只开发移动端,没有Web端。移动互联网的时代造就“大移动端”,Flutter做为一款能作到媲美原生的高性能跨平台技术方案,或许一统天下。

  在跨平台技术领域,只要挑战在,技术就不会停滞,伴随着技术不断演进与革新,终将走向美好。

  6. Flutter技术优点

  Flutter是完全的跨平台方案,既没有采用WebView,也没有采用JS桥接原生控件,而是自行实现一套UI框架,在引擎底层经过Skia渲染到屏幕。对于UI以外所须要使用的移动设备自身提供的服务,好比相机、定位、屏幕触摸等,则采用Platform Channels跟原生系统通讯的方式来实现。

  对于Flutter优点,回到前面讲到移动端技术选型的4要素,研发效率、动态性、多端一致性、性能体验,分别对应下面这一组词语。
4.jpg
  高效率:采用dart语言编写代码,虽然刚开始上手须要点时间,但熟练后效率比较高。一套代码适用多个平台(Android、iOS、Web),以及高效的Hot Reload能快速辅助调试;

  动态化:2017年3月苹果下发警告邮件,禁止JSPatch等 iOS App热更新方案,今后iOS动态化成为一个不宜公开讨论的话题。一样地,Flutter引擎在某一个官方版本对动态化作过一些尝试,但后续基于风险考虑移除,固然并无阻碍你们对技术的探索,这里不方便展开讨论;

  高一致性:实现UI像素级的控制,Flutter渲染引擎依靠跨平台Skia图形库来实现,仅依赖系统图形绘制相关的接口,好比将来Android会支持vulkan,iOS会支持metal,这些都是经过skia封装调用。可最大程度上保证不一样平台的体验一致性,见下图所示。
5.jpg
  高性能:渲染性能优于现有的各类跨平台框架,可媲美原生性能的跨平台技术方案,Dart代码执行效率比JS高,经过AOT编译成平台原生代码,渲染采用自渲染skia方案,既不须要JS Bridge桥接,也不须要Art虚拟机参与。再从渲染原理来看看Flutter的高性能的底气在哪里。
6.jpg
  图解:

  Android原生框架,经过调用Java Framework层,再调用到skia来渲染界面;

  其余跨平台方案(如RN),经过JSBridge中间层来将JS写的APP转换成相应的原生渲染逻辑,可见比Native代码增长了更多逻辑,性能逊色差于原生框架;

  Flutter框架,APP经过调用Dart Framework层,再直接调用到skia来渲染界面,并无通过原生Framework过程,可见其渲染性能并不会弱于Native技术,这是一个性能上限很高的跨平台技术。

  固然,不得不说目前的Flutter确实不够尽善尽美,会存在一些不够尽善尽美之处,好比生态不够健全,包体积问题,但其该方案的上限比较高,想象空间比较大,相信更多开发者参与进来,通过更多打磨,将来会作得更好。

  7. 业界发展近况

  2017年5月Google I/O大会正式对外公布Flutter,到2018年12月发布Flutter1.0,引起全球大量的开发者和企业开始研究Flutter。StackOverflow 2019年的全球开发者文件调查中,Flutter被评选为最受开发者欢迎的框架之一,超过了TensorFlow和Node.js。
7.jpg
  目前,全球愈来愈多的公司已经在你们耳熟能详的知名APP中使用Flutter技术并落地,尤为国内知名互联网公司对Flutter投入度很大,社区也是很是活跃。
8.jpg
  8. Flutter将来趋势

  目前Flutter主要在移动Android/iOS跨双端,Flutter 的愿景是成为一个多端运行的 UI 框架,可以支持不只仅是移动端,还包括Web、桌面、甚至嵌入式设备。在2019 Google I/O 开发者大会上推出的使用 Flutter 开发 Web 应用的框架,同年9月发布Flutter 1.9,并将Flutter web合入Flutter主仓库。
9.jpg
  从架构图看,Flutter采用同一个Dart Framework层来统一Flutter C++引擎和Web引擎,最终能够运行在Android,iOS,Browser上,从Flutter引擎代码不难看出Flutter也是支持Fuchsia操做系统。

  《 Android技术架构演进与将来 》文中已介绍过,Fuchsia是Google内部正在开发的一款新的操做系统,采用Flutter做为系统默认的UI框架,也就是说Flutter自然支持Fuchsia,这无疑让Flutter在众多的跨平台方案更有优点。

  从Fuchsia技术架构来看,内核层zircon的基础LK是专为嵌入式应用中小型系统设计的内核,代码简洁,适合嵌入式设备和高性能设备,好比IOT、移动可穿戴设备等,目前这些领域尚未标准化级别的垄断者。以及在框架层中有着语音交互、云端以及智能化等模块,由此笔者揣测将来Fuchsia率先应用在音控等智能嵌入式设备。
10.jpg
  目前你们广泛比较看好的将来两个技术就是5G和IoT时代。对于5G的需求,很大程度上是由于移动互联网发展到“IoT时代”的阶段。这个发展阶段,全球上网设备的数量可能会达到500亿个。随着5G+IOT时代的到来,如今你们比较关注的Flutter包大小也一样再也不是一个问题,或许Flutter技术的生命期比客户端更长,或许Fuchsia正在驰骋IOT疆场,你所掌握的Flutter技术栈能够无缝迁移,一次弯道超车的机会。

  到此,介绍完跨平台技术演进以及Flutter的优点。看到这,相信你可能对Flutter技术有必定兴趣,为了能让你们快速了解Flutter内部原理而不枯燥,本文不听任何的源码,经过一系列图来帮你们从总体架构来快速理解Flutter。

  2、Flutter引擎架构1. Flutter技术架构

  先来看看Flutter总体的技术架构,分为四层,从上之下依次是Dart APP,Dart Framework, C++ Engine,Platform。
11.jpg
  Flutter架构最核心的即是Framework(框架)和Engine(引擎):

  Flutter Framework层:用Dart编写,封装整个Flutter架构的核心功能,包括Widget、动画、绘制、手势等功能,有Material(Android风格UI)和Cupertino(iOS风格)的UI界面, 可构建Widget控件以及实现UI布局。

  Flutter Engine层:用C++编写,用于高质量移动应用的轻量级运行时环境,实现了Flutter的核心库,包括Dart虚拟机、动画和图形、文字渲染、通讯通道、事件通知、插件架构等。引擎渲染采用的是2D图形渲染库Skia,虚拟机采用的是面向对象语言Dart VM,并将它们托管到Flutter的嵌入层。shell实现了平台相关的代码,好比跟屏幕键盘IME和系统应用生命周期事件的交互。不一样平台有不一样的shell,好比Android和iOS的shell。

  2. Flutter编译产物

  看完Flutter内部架构,或许你好奇,Flutter不用Android/iOS的本地语言技术开发,Dart编写完的代码如何让不一样系统能够识别,最终编译后获得的产物是什么呢?
12.jpg
  Flutter产物分为Dart业务代码和Engine代码各自生成的产物,图中的Dart Code包含开发者编写的业务代码,Engine Code是引擎代码,若是并无定制化引擎,则无需从新编译引擎代码。

  一份Dart代码,可编译生成双端产物,实现跨平台的能力。通过编译工具处理后可生成双端产物,图中即是release模式的编译产物,Android产物是由vm、isolate各自的指令段和数据段以及flutter.jar组成的app.apk,iOS产物是由App.framework和Flutter.framework组成的Runner.app。

  这个过程涉及frontendserver、gensnapshot、xcrun、ninja编译工具。frontendserver前端编译器会进行词法分析、语法分析以及相关全局转换等工做,将dart代码转换为AST(抽象语法树),并生成app.dill格式的dart kernel。gensnapshot通过CHA、内联等一系列执行流的优化,根据中间代码生成优化后的FlowGraph对象,再转换为具体相应系统架构(arm/arm64等)的二进制指令。

  3. Flutter引擎启动

  既然了解了Flutter的编译产物,那你或许又好奇,Flutter这台引擎如何发动的,怎么跟Native衔接呢?
13.jpg
  这里以Android为例,熟悉Android的开发者,应该都了解APP启动过程,会执行Application和Activity的onCreate方法,FlutterApplication和FlutterActivity的onCreate方法正是链接Native和Flutter的枢纽。

  FlutterApplication.java的onCreate过程主要完成初始化配置、加载引擎libflutter.so、注册JNI方法;

  FlutterActivity.java的onCreate过程,经过FlutterJNI的AttachJNI方法来初始化引擎Engine、Dart虚拟机、Isolate、taskRunner等对象。再通过层层处理最终调用main.dart中main方法,执行runApp(Widget app)来处理整个Dart业务代码。

  Flutter引擎启动中会建立有4个TaskRunner以及建立虚拟机,分别来看看它们的工做原理。

  4. TaskRunner工做原理

  Flutter引擎启动过程,会建立UI/GPU/IO这3个线程,会为这些线程依次建立MessageLoop对象,启动后处于epoll_wait等待状态。对于Flutter的消息机制跟Android原生的消息机制有不少类似之处,都有消息(或者任务)、消息队列(或任务队列)以及Looper;有一点不一样的是Android有一个Handler类,用于发送消息以及执行回调方法,相对应Flutter中有着相近功能的即是TaskRunner。
14.jpg
  上图是从源码中提炼而来的任务处理流程,比官方流程图更容易理解一些复杂流程的时序问题,后续会专门讲解个中起因。Flutter的任务队列处理机制跟Android的消息队列处理相通,只不过Flutter分为Task和MicroTask两种类型,引擎和Dart虚拟机的事件以及Future都属于Task,Dart层执行scheduleMicrotask所产生的属于Microtask。

  每次Flutter引擎在消费任务时调用FlushTasks方法,遍历整个延迟任务队列delayedtasks,将已到期的任务加入task队列,而后开始处理任务。

  Step 1: 检查task,当task队列不为空,先执行一个task;

  Step 2: 检查microTask,当microTask不为空,则执行microTask;不断循环Step 2 直到microTask队列为空,再回到执行Step 1;

  可简单理解为先处理完全部的Microtask,而后再处理Task。由于scheduleMicrotask方法的调用自身就处于一个Task,执行完当前的task,也就意味着立刻执行该Microtask。

  了解了其工做机制,再来看看这4个Task Runner的具体工做内容。

  Platform Task Runner:运行在Android或者iOS的主线程,尽管阻塞该线程并不会影响Flutter渲染管道,平台线程建议不要执行耗时操做;不然可能触发watchdog来结束该应用。好比Android、iOS都是使用平台线程来传递用户输入事件,一旦平台线程被阻塞则会引发手势事件丢失。

  UI Task Runner: 运行在ui线程,好比1.ui,用于引擎执行root isolate中的全部Dart代码,执行渲染与处理Vsync信号,将widget转换生成Layer Tree。除了渲染以外,还有处理Native Plugins消息、Timers、Microtasks等工做;

  GPU Task Runner:运行在gpu线程,好比1.gpu,用于将Layer Tree转换为具体GPU指令,执行设备GPU相关的skia调用,转换相应平台的绘制方式,好比OpenGL, vulkan, metal等。每一帧的绘制须要UI Runner和GPU Runner配合完成,任何一个环节延迟均可能致使掉帧;

  IO Task Runner:运行在io线程,好比1.io,前3个Task Runner都不容许执行耗时操做,该Runner用于将图片从磁盘读取出来,解压转换为GPU可识别的格式后,再上传给GPU线程。为了能访问GPU,IO Runner跟GPU Runner的Context在同一个ShareGroup。好比ui.image经过异步调用让IO Runner来异步加载图片,该线程不能执行其余耗时操做,不然可能会影响图片加载的性能。

  5. Dart虚拟机工做

  Flutter引擎启动会建立Dart虚拟机以及Root Isolate。DartVM自身也拥有本身的Isolate,彻底由虚拟机本身管理的,Flutter引擎也没法直接访问。Dart的UI相关操做,是由Root Isolate经过Dart的C++调用,或者是发送消息通知的方式,将UI渲染相关的任务提交到UIRunner执行,这样就能够跟Flutter引擎相关模块进行交互。

  何为Isolate,从字面上理解是“隔离”,isolate之间是逻辑隔离的。Isolate中的代码也是按顺序执行,由于Dart没有共享内存的并发,没有竞争的可能性,故不须要加锁,也没有死锁风险。对于Dart程序的并发则须要依赖多个isolate来实现。
15.jpg
  图解:

  isolate堆是运该isolate中代码分配的全部对象的GC管理的内存存储;

  vm isolate是一个伪isolate,里面包含不可变对象,好比,true,false;

  isolate堆能引用vm isolate堆中的对象,但vm isolate不能引用isolate堆;

  isolate彼此之间不能相互引用;

  每一个isolate都有一个执行dart代码的Mutator thread,一个处理虚拟机内部任务(好比GC, JIT等)的helper thread;可见,isolate是拥有内存堆和控制线程,虚拟机中能够有不少isolate,但彼此之间内存不共享,没法直接访问,只能经过dart特有的Port端口通讯;isolate除了拥有一个mutator控制线程,还有一些其余辅助线程,好比后台JIT编译线程、GC清理/并发标记线程;

  6. Widget架构概览

  Flutter引擎启动后执行Dart业务,是经过runApp(Widget app)方法,那Widget又是什么呢?
16.jpg
  Widget是全部Flutter应用程序的基石,Widget能够是一个按钮,一种字体或者颜色,一个布局属性等,在Flutter的UI世界可谓是“万物皆Widget”。常见的Widget子类为StatelessWidget(无状态)和StatefulWidget(有状态);

  StatelessWidget:内部没有保存状态,UI界面建立后不会发生改变;

  StatefulWidget:内部有保存状态,当状态发生改变,调用setState方法会触发StatefulWidget的UI发生更新,对于自定义继承自StatefulWidget的子类,必需要重写createState方法。

  三棵树
17.jpg
  图解:

  Widget是为Element描述须要的配置, 负责建立Element,决定Element是否须要更新。Flutter Framework经过差分算法比对Widget树先后的变化,决定Element的State是否改变。当重建Widget树后并未发生改变, 则Element不会触发重绘,则就是Widget树的重建并不必定会触发Element树的重建。

  Element表示Widget配置树的特定位置的一个实例,同时持有Widget和RenderObject,负责管理Widget配置和RenderObject渲染。Element状态由Flutter Framework管理, 开发人员只需更改Widget便可。

  RenderObject表示渲染树的一个对象,负责真正的渲染工做,好比测量大小、位置、绘制等都由RenderObject完成。

  可见,开发者经过Widget配置,Framework经过比对Widget配置来更新Element,最后调度RenderObject Tree完成布局排列和绘制。

  7. 渲染原理

  Dart的UI采用Widget来实现,最终转换为RenderObject,那界面又是如何渲染的呢?
18.jpg
  渲染过程,UI线程完成布局、绘制操做,生成Layer Tree;GPU线程执行合成并光栅化后交给GPU来处理,其中几个关键步骤:

  Animate: 遍历_transientCallbacks,执行动画回调方法;

  Build: 对于dirty的元素会执行build构造,没有dirty元素则不会执行,对应于buildScope

  Layout: 计算渲染对象大小和位置,对应于flushLayout,这个过程可能会嵌套再调用build操做;

  Compositing bits: 更新具备脏合成位的任何渲染对象, 对应于flushCompositingBits;

  Paint: 将绘制命令记录到Layer, 对应于flushPaint;

  Compositing: 将Compositing bits发送给GPU, 对应于compositeFrame;

  GPU线程经过skia向GPU硬件绘制一帧的数据,GPU将帧信息保存到FrameBuffer里面,而后视频控制器会根据VSync信号从FrameBuffer取帧数据传递给显示器,从而显示出最终的画面。

  8. Platform Channels

  Flutter框架提供了UI的控件支持,对于APP除了UI还有其余依赖于Native平台的支持,好比调用Camera的功能,该怎么办呢?为此,Flutter经过提供Platform Channel的功能,使得Dart代码具有与Native交互的能力。
19.jpg
  Platform Channel用于Flutter与Native之间的消息传递,整个过程的消息与响应是异步执行,不会阻塞用户界面。Flutter引擎框架已完成桥接的通道,这样开发者只需在Native层编写定制的Android/iOS代码,便可在Dart代码中直接调用,这也就是Flutter Plugin插件的一种形式。

  3、结束语

  科技不断在进步,技术不断发展,移动跨平台技术几乎从Android、iOS诞生不久便出现,已发展快10年。时至今日,兼具跨端高效率与高性能体验的Flutter力压群雄,崭露头角,已然成为当下最热门的移动端新技术,全球愈来愈多的公司在Flutter技术布局并落地产品应用,社区也很是活跃。

  笔者以前一直从事于Android操做系统底层研发工做,今年刚接触Flutter,Flutter做为一门全新的跨平台技术框架,不断深究会发现这是一个小型系统,涉及到的技术很广:

  编译技术如何将dart代码转换为AST(抽象语法树),如何汇编转换为机器码,打包成产物是什么?

  Flutter这台引擎如何发动的,怎么跟Native原生系统衔接运行,如何识别产物并加载到内存?

  引擎启动后,TaskRunner如何分发任务,跟原生系统消息机制有什么关系?

  Dart虚拟机如何管理内存,跟isolate又有什么关系?

  开发者编写的Widget控件如何渲染到屏幕上?

  Flutter如何经过plugin支持移动设备提供的服务?

  以上问题本文都已逐一回答,若是仅仅是用Flutter作业务开发,并不须要掌握这么深度技术,不过,知其然知其因此然,能让你游刃有余。本文讲述跨平台技术的过去与将来,以及从宏观架构解读Flutter内部原理,后续有时间将更深刻的技术细节以及实战经验角度来跟你们揭秘更多Flutter技术。

  随着5G+IOT时代的到来,Fuchsia系统或许发力IOT新战场,你所掌握的Flutter技术栈能够无缝迁移,这是一次弯道超车的机会。即使Fuchsia落败,相信只要深扎Flutter系统技术的精髓,其余任何的移动端新技术均可以轻松快速地掌握。

  最后,用一句话来结束本次分享,“有时候,你选择一个方向,不是由于它必定会成为将来,而是它有可能成为不同的将来。”