1.2 Flutter技术
前面小节中介绍了业界比较流行的跨端框架,以及跨端技术的实现难点。Flutter一经推出,由于它在技术上的突破性,迅速在跨端领域脱颖而出,并在短时间内成为技术热点。本节将对Flutter技术进行整体介绍。
1.2.1 Flutter技术简介
Flutter是由Google公司研发的一种跨端开发技术,其最初目的是用于Google内部正在研发的一款新操作系统Fuchsia,Flutter是作为这个新系统的UI框架。2017年在Google I/O大会上,Flutter框架作为一种能够开发Android、iOS应用的跨端框架正式对外公布。2018年Flutter1.0正式推出,标志其功能走向稳定。
Flutter在跨端技术的多个难点问题上都有突破,它的优势具体可以梳理为以下几个方面。
1 像素级别的双端一致性
Flutter自带Skia图形绘制引擎,采用自绘制方式,不论在Android还是iOS上,Flutter应用的渲染都不是系统原生,都统一用Flutter的Skia引擎进行绘制。因此两端渲染过程是完全一致的,能够实现像素级别双端一致性。
双端一致性是跨端框架中的难点问题,在Flutter中通过自绘制的方式得到完美解决,不仅UI实现了一致展示,动画、动效等都能达到像素级别的双端一致性。
2 接近原生的执行效率
跨端框架的运行效率通常低于原生,而Flutter针对这一难题实现了突破,达到了接近原生的执行效率,远远超出同类框架。
首先在编程语言上,Flutter采用Dart语言,这是一种非常先进的编程语言,支持多种编译方式,既能够以JIT方式编译,也能够以AOT方式编译。其中JIT用于开发阶段,像JavaScript一样动态执行,虽然运行效率低,但可以热重载(Hot Reload)。Release模式使用AOT,它能将Dart代码编译为平台原生代码,运行时无须再通过解释器解释,因此执行效率远高于JavaS-cript,接近原生。
第二点在绘制引擎上,Flutter采用集成Skia引擎自渲染,实现了从执行到渲染的闭环,没有跨层带来的性能损耗。所谓跨层,以React Native框架为例,在React Native中进行UI渲染,需要通过JS Bridge将绘制任务从JavaScript引擎中,经由C/C++层转移到Objective-C/Java层,这会导致一定的性能开销。而对于Android来说,从C/C++层转移到Java层又涉及JNI调用,会带来额外的性能开销。由于Flutter绘制没有跨层,保障了从执行到渲染都在底层高效率完成,没有额外的性能损失。综合以上几点,Flutter实现了接近原生的执行效率。
3 高度双端代码复用
对于跨端框架来说,代码复用率是一个核心考量指标,毕竟跨端的目的就在于此。跨端应用代码可以分为三部分:业务逻辑代码、UI视图代码,以及两端差异化适配代码。
在React Native中,由于基于JavaScript引擎和React生态进行应用开发,能够实现业务逻辑的完全复用。但React Native的底层视图层是基于两端原生视图进行封装导出,严格来说这只是做到了接口复用,并没有实现底层视图代码的复用。
在Flutter中,由于采用自渲染方案,两端从运行时环境到底层渲染都完全一致,因此可以实现最大化的双端复用。在实际工作中,基本90%的代码能够实现双端复用,剩下的10%即两端差异化适配代码。
4 更高的开发效率
Flutter带来了一套全新的开发模式,这套开发模式非常优秀,因此带来了远高于原生开发的开发效率,主要体现在以下几个方面。
Flutter选用Dart语言进行开发,Dart语言是一门容易上手、功能强大的语言。容易上手体现在其语法与Java、JavaScript非常相似,开发者只要熟悉其中一种语言就能够快速上手。同时Dart是一门强类型语言,这对于开发大型商业应用来说是必需的。Dart也是一门功能强大的编程语言,不论是函数式编程、OOP编程、还是泛型、异步,都能够非常好的支持。从语言层面,不论是简单功能还是复杂功能,使用Dart开发都能实现较高的开发效率。
Flutter借鉴React设计了一套自己的组件式框架。组件式框架是前端开发效率高于传统原生的一个重要因素。同时Flutter提供了大量功能、布局组件,开发者可以快速实现UI布局,同时基于组件式设计让应用具备较好的架构分层。
传统开发效率之所以低,大部分时间都浪费在了编译上。尤其是在UI开发中,有时不得不反复编译应用调试效果,每次编译耗费的时间少则几分钟,多则十几分钟。Flutter在开发阶段使用JIT模式编译Dart代码,通过Hot Reload特性,代码更改可以直接应用到设备中,实现“亚秒级”的效果实时展示,大幅提高了开发效率。
5 跨端动画效果
移动应用强调用户体验,其中动画效果起到很大作用,动画为用户带来了赏心悦目、流畅的操作体验。跨端框架的动画开发效率和运行效率在开发初期容易被忽视,但实际是十分重要的衡量因素。
动画对性能有比较高的要求,尤其是复杂动画,需要以60帧甚至更高的帧率运行才能保证流畅效果。网页跨端方案运行在WebView中,由于执行效率低,复杂动效的体验不佳。Re-act Native有多套动画框架,有的采用纯JavaScript实现,因此执行效率也较低,有的采用桥接到原生实现,同样存在从JavaScript桥接到原生的性能折损,同时由于实现更加复杂,会降低开发效率。
动画的开发效率取决于动画框架,在原生开发中系统提供的动画框架易用度不高,开发成本较高。因此原生开发诞生了许多优秀的第三方开源库,简化了动画实现的开发成本,但由于两端的动画底层框架不统一,在实现双端对齐时仍需要一定的开发成本。
Flutter由于自建绘制引擎,在动画的性能上有先天优势,能够实现接近原生的执行效率,这是超越同类跨端框架的。
在开发效率上,Flutter提供了一套功能强大、简单易用的动画框架,能够方便地实现各种动画效果。同时Flutter的动画效果也是双端像素级别对齐的,实现了真正的动画代码双端复用。
6 先进的应用架构理念
前端领域经过近年的快速发展,在应用架构理念上诞生了许多新的最佳实践,比如组件式框架和全局状态管理器等,原生开发相比之下处于落后阶段。应用架构模式决定了复杂项目的业务分层和职责划分,好的架构能够保障业务代码健康有序地增长,提高可维护性。
对于一个复杂的商业项目,后期的开发效率不仅取决于技术选型,更多地取决于架构设计的合理性。早期的架构设计不合理造成的技术债务,会影响到后期的功能开发。
Flutter虽然没有直接采用前端JavaScript生态,但它在设计中大量借鉴了前端架构理念,不论是组件化框架还是全局状态管理器均有Dart下的实现。前端流行的架构模式在Flutter下均有对应实现,同时Flutter开源生态中也发展出了带有Flutter特色的新架构模式。通过这些架构模式,Flutter具备开发复杂的商业项目的架构基础。
7 未来发展潜力巨大
从2018年正式推出至今,仅两年多时间Flutter就获得了如此大的发展与关注,从客观说明了Flutter技术极具潜力。
创新性的技术选型,以及对跨端技术难点问题带来的突破性改进使Flutter成为移动跨端领域的佼佼者,越来越多的应用基于Flutter开发,越来越多的互联网巨头投入到Flutter的发展中。
Flutter的技术特点使其跨端能力极强,不仅能够跨移动端,也能够运行在Web端及桌面平台。更广泛的跨端能力是Flutter当下发展的重点。在Google的规划中,Flutter已不仅是一个移动端跨端框架,而是要成为一个跨Web、桌面、移动端的全覆盖跨端框架。
对于读者来说,学习Flutter是一项非常值得的投资,通过学习Flutter,不仅能够具备出色的跨端开发能力,在未来随着全覆盖跨端的完善,直接获得了全平台开发能力,投入回报非常高。
同时,Flutter还是Google下一代操作系统Fuchsia的UI框架,虽然这个系统目前仍处于试验阶段,未来是否能够成功还是一个未知数。假设Fuchsia未来有一天替代了Android系统,此时对Flutter的学习投入就更加有价值,让自己先人一步,掌握先机。
1.2.2 Flutter整体架构
前文对Flutter进行了整体介绍,相信读者对Flutter的Skia自渲染、组件式框架,以及这些技术所带来的优势有了初步的了解。但是这些技术是如何组织到一起,如何共同支撑起Flutter的功能?本小节通过介绍Flutter整体架构,对这些问题进行解答。
Flutter整体架构可以分为3层,从上到下分别是框架层(Framework)、引擎层(Engine),以及嵌入层(Embedder),如图1-5所示。
下面按照自底向上的方式,对各层进行介绍。
1 嵌入层
Flutter是一个跨端框架,Flutter将适配一个操作系统所需的原生能力抽象出来作为嵌入层。因此在任何一个操作系统上,只要实现嵌入层接口,就实现了Flutter在这个平台上的跨端能力。
图1-5 Flutter整体架构
原生系统需要向Flutter提供一个Surface视图用于UI展示,Flutter会像“放电影”一样将Flutter应用的UI投射到这个视图上。原生系统还需要处理用户的输入手势,并转为Flutter手势格式传给Flutter。除此之外,原生系统还要为Flutter准备好其所需的线程和消息队列,以及一些原生能力的封装与导出。
嵌入层的实现语言与平台相关,例如,在Android上使用Java和C++完成适配,在iOS上使用Objective-C/Objective-C++完成适配。
2 引擎层
引擎层顾名思义,它驱动了Flutter应用的执行,是Flutter的核心,由C/C++实现。引擎层中包含了Dart运行时和Skia底层绘制库,分别用于执行Dart代码、底层布局、文本等绘制工作。
引擎层负责帧调度及UI的底层绘制工作。它提供了Flutter核心API的底层实现,包括图形绘制(通过Skia引擎)、文本布局、文件和网络I/O、可访问性支持、插件架构,以及Dart运行时和编译工具链。
这些能力运行在嵌入层提供的能力之上,使用C++进行实现,最终被包装为Dart库供上层调用,如dart:ui库。
3 框架层
框架层实现了Flutter的应用层框架,前文中说到的Flutter组件化框架、动画框架、UI组件库等均位于这一层。这一层的代码均使用Dart语言开发,开发者日常与Flutter打交道的也是这一层。
在这一层中,自底向上又可细分为一系列内部层次。
首先是Foundation定义了Flutter应用框架中最基础的类,比如AbstractNode抽象节点、ChangeNotifier通知监听器、组件标识Key等。这些类是支撑Flutter应用框架功能的基础。
往上一层包含三部分内容,分别是动画、绘制与手势,它们提供了Flutter的UI基础。
基于UI基础来到了Rendering渲染层,它提供了布局能力,并定义了RenderObject树的概念,通过这套机制可以确定UI组件在界面中的位置、大小,以及如何绘制。
渲染层之上是组件层,它为Flutter带来了组件化的开发模式。在Flutter开发中,“一切皆组件”都是基于这一层所定义的组件框架实现。Flutter中的组件采用响应式编程的开发模式,也是在这一层中实现的。
基于组件的概念,Flutter提供了大量现成的组件供开发者使用,这些组件提供了方方面面的功能,大大提高了开发者的开发效率。其中,包含了大量UI组件,这些UI组件整体可分为两个组件库Material和Cupertino,分别对应Android和iOS的设计风格。开发者在进行UI开发时,可根据自己应用的设计风格,选择使用相应组件库中的UI组件。
1.2.3 Flutter与同类方案对比
在完成了对Flutter框架的介绍后,本小节再对Flutter与已有跨端框架进行横向对比,相信通过对比,读者能够对Flutter的技术特点有更加深入的理解。
1 Flutter与基于WebView方案对比
基于WebView方案包括网页跨端与小程序等方案。由于WebView执行效率较低,因此自建引擎的Flutter在执行效率上大幅领先这一类方案,且接近于原生效率。
在双端代码复用方面,WebView方案基于HTML/CSS/JavaScript三剑客,或者基于这三者实现的优化方案,能够实现双端高度代码复用。Flutter基于自身Flutter应用框架,也能够实现高度代码复用。因此在这一点上,两者不相上下。
在开发效率方面,WebView方案基于前端开发生态,具备较高的开发效率。Flutter应用框架借鉴自前端,因此同样也具备较高的开发效率,因此也是不相上下。
从原生能力导出方面,WebView方案的JSBridge方式要相对弱一些,Flutter基于Channel机制建立Plugin生态,有大量开源Plugin封装了各种原生能力可直接复用,因此Flutter在原生能力集成方面更为优异。
从动态性方面,WebView方案是目前动态性最好的方案,Flutter则默认不支持动态性,从这一点上WebView方案胜出。
整体来说,Flutter的性能远高于WebView方案,因此能够胜任更多对性能体验有要求的场景。同时Flutter的原生扩展生态更为强大,基于原生生态能够更好地开发出原生体验的应用。但对于强调动态化的场景,则WebView方案是更好的选择。
2 Flutter与基于原生导出方案对比
原生导出方案包括React Native、Weex等,是最终通过平台原生能力进行渲染的方案。
这类方案通常会选用JavaScript引擎进行动态脚本解析,JavaScript脚本的执行效率低于原生,同时由于需要桥接到原生进行渲染,存在通信成本,因此整体运行效率也远远低于Flutter。
这类框架还存在一个架构难题,即跨端一致性。由于是对双端的原生视图组件进行封装,实现接口层复用,而双端视图组件的一些实现细节是难以对齐的,因此如何实现高度一致是这类框架的一个难点。自渲染的Flutter引擎天生不存在这一问题,能够实现像素级的多端一致。
在开发效率方面,React Native基于React框架,Flutter框架也是大量借鉴了React,因此在开发效率方面两者基本是一致的。
在动态性方面,React Native框架基于JavaScript引擎,可以实现动态化。Flutter底层是支持动态化的,但Google并没有将这个能力开放出来。在这一点上React Native更为突出。
从整体来说,Flutter的性能依旧高于原生导出方案,并且最大的优势在于先天的多端一致性,这是原生导出方案难以完美解决的。因此,如果不考虑动态性,Flutter会是更好的方案。
值得一提的是,React Native也是目前主流的跨端方案之一,经过多年发展,虽然在多端一致性方面难以实现完美,但也能够保证高度一致。同时React Native完全基于前端生态,前端生态是整个大前端领域中最有活力的,大量新技术层出不穷。同时由于基于Web技术栈,也能够与Web开发技能进行较好的迁移复用。因此目前React Native依旧吸引了大量开发者。
由此也可看出,Flutter为跨端开发领域带来了新的可能性,因此引发了一轮热潮,而已有老牌框架依旧焕发着生机,移动跨端领域呈现出百花齐放的状态。这表明了移动跨端领域的勃勃生机,不断向生产力更高的方向快速发展。