先说点废话。今天的主题关于跨平台开发技术的。跨平台开发大家应该都了解,无非就是写一套代码可以运行在两个甚至多个平台之上,而对于我们客户端/前端开发来说,跨平台一般就是跨现在主流的Android和iOS两个移动端平台,再牛逼一点的可以加上Web浏览器。
目前业内也有很多跨平台解决方案,例如Hybird、ReactNative、微信小程序、阿里的Weex、微软的Cordova、Xamarin等。其中一些方案甚至被叫嚣要取代原生开发,当然目前看来是不可能的。因为尽管前面说的几种跨平台方案可以做到多端复用,动态更新,但是都避不开老生常谈的问题,就是在高要求下的加载渲染效率和复杂动画交互的实现,它们做的都不如原生应用。
然后再说今天和大家分享一个开发框架Flutter,它与其他跨平台方案都不同,一统天下可能很难,但是我觉得是目前最优的跨平台解决方案。
官方定义:
Flutter是Google发布的一款UI开发工具集,开发语言是Dart。用它可以为移动端、web浏览器、PC桌面构建漂亮的,本机编译的应用程序,关键是用的同一套代码。
在这里大家比较关注的点,应该是一套代码可以运行在不同的平台上。官方说明的手机平台只包含了Android和iOS系统,等后面大家了解完Flutter跨平台原理后就会发现,Flutter构建的应用应该可以运行在其他移动操作系统上,例如阿里的云OS,甚至是华为新推出的鸿蒙系统,甚至是Web和PC平台上。
由于Web浏览器的特殊性(因为Web应用只能访问浏览器提供的API,而无法直接访问系统API),所以Flutter在Web浏览器上运行的机制和其他平台不一样,这里先简单说一下。
我们都知道,在浏览器运行的web页面必须符合三大标准,即HTML、CSS和JavaScript,通过这三剑客来实现页面呈现和业务逻辑处理,其他的东西浏览器是不承认的。
大家看一下这张图,Framework层就用Dart编写的,面向开发者的API,可以实现业务布局和业务逻辑,这一部分在不同的平台上是一样的。主要是看下面的dart:ui部分是如何实现。Flutter Web Engine是Flutter开发团队自己实现的一个Web引擎,简单来说就是把Dart编写的页面转换成浏览器认识的HTML、CSS和JavaScript。当然,这个转换过程并不是一对一映射((完整说明见https://medium.com/flutter/hummingbird-building-flutter-for-the-web-e687c2a023a8)),而是根据不同的内容选择不同的图片实现(HTML + CSS + Canvas )。
Dart Code -> JS -> HTML + CSS + Canvas -> 浏览器渲染
历史和现状:
2014.10 Flutter的前身Sky在Github上开源
2015.10 经过一年的开源,Sky正式改名为Flutter。在Dart开发者峰会上发布了第一个版本。
2018.02 在世界移动大会(MWC)发布首个Beta版
2018.06 在(GMTC)发布首个预览版
2018.12 在Flutter Live2018上发布1.0稳定版。
变更:
全新iOS风格的widget
接入约20种Firebase服务
使用Dart 2.1,生成更小的代码和更快的速度
2019.02 在世界移动大会(MWC)发布1.2
变更:
提升框架性能和质量
视频播放、WebView、Maps等bug修复
支持Android App Bundle
基于Web的编程工具套件
支持前端开发者使用HTML和CSS语法编写应用
2019.05 在Google IO大会上发布1.7,也是目前最新版本。
变更:
支持Android X
支持32位和64位的Android App Bundle和Apk
更新小部件RangeSlider以及更复杂的排版
支持游戏控制器
趋势:
[外链图片转存失败(img-vE5IEYDG-1566562914259)(media/15454717435900/3465086455-5d0cafa0245c7_articlex.jpeg)]
从Github上的Star数来看,在Google刚推出Flutter时发展很缓慢,直到18年2月发布首个Beta版本后迎来了爆发性增长,12月发布第一个正式后增长更快。目前在Github上,Flutter项目的Star数为72895,要知道Facebook的react-native的Star数为80113.
由于Flutter的诸多优势,我们熟悉的很多大厂都开始应用Flutter到自家的产品中。
例如Google旗下的Google Ads、阿里旗下的闲鱼、腾讯旗下的NOW直播、头条的抖音等等。还有我司现在正在接入的Square公司的刷卡功能,Square都已经提供了Flutter版本的SDK。
尽管许多大厂开始将Flutter应用到商业中,但并不代表Flutter已经非常完美和稳定了。比如一些非常关键的功能还仍处于预览状态,包括将Flutter和原生的混合开发;在Web浏览器中运行Flutter应用。
通过对Eric的访谈可以知道:
Eric Seidel是Flutter团队的头头。Eric之前是做Web开发的,在Chrome团队工作,并且很擅长图像渲染这一领域。在某段时间,他们遇到了一些问题,他们想让Web中的一部分能够拥有更加平滑的体验,于是他们做了一个试验,考虑到不一定非要与Web兼容,于是开始删代码,删除那些为了兼容旧的访问方式的支持代码,把Web开发中不常用的内容全部删除。最后对精简后代码做了一次基准测试,发现效率提高了20倍。
再之后研究就属于Flutter项目的范畴了。
在2015年4月的Dart开发者会议上,Eric分享了Sky项目,也就是Flutter前身,并且现场用Android手机演示了用Dart写的应用程序,声称达到120fps的渲染效率。
Flutter框架的整体架构也是分层的,貌似分层架构是解决复杂项目实现的不二法则。看到这张图让我想到了Android系统的框架结构、网络协议栈的结构、还有我们App的项目结构,呵呵。
Flutter整体氛围三层:
1、框架层,由Dart编写。
2、引擎层,由C/C++编写。
3、平台嵌入层,由平台对应语言实现。
通过这张架构图可以看出,Flutter中平台相关的特性很少,所以当宿主系统升级时,Flutter不需要很多的适配开发工作。
Widgets是UI组件部分,其中Material和Cupertino是对Widgets的封装,方便开发者实现不同的风格的App,减少开发量。
框架层的Rendering和引擎中的Rendering模块代表了Flutter对视图树的抽象、布局、绘制的逻辑,也就是说Flutter的UI框架的图像渲染是自己做的,既不依赖WebView,也不委托给原生。
再往下的Animation、Painting、Gestures是Flutter提供的动画、绘图、手势处理的Api。
Foundation 是一些Dart语言提供的一些常用的库,例如网络和IO。
Platform Channels + Native Plugins实现了Dart语言和原生语言的通信机制。
SystemEvents来处理系统事件,比如触摸、键盘输入、屏幕反转等。
Service Protocol 扩展服务,例如性能检测、应用信息追踪、调试
Composition 多个图层合并,提交渲染效率
Dart VM Management,Dart虚拟机管理。
Frame Scheduling和Frame Pipelining负责帧图像的调度和排序。
TextLayout则专门负责文字排版
因为Flutter本身还是依附在一个原生的页面上的,比如Android中的Activity、iOS中的ViewController。而且系统的Api访问还是得通过原生环境桥接一下。
所以不可避免的会对不同平台做对应的适配。
Render Surface Setup,设置原生环境的渲染图层,例如Android中的SurfaceView。
Packaging,打包成对应平台的安装包时,需要把上面两层东西一并打包。
Thread Setup,其实Flutter的运行线程是由系统的线程策略实现的。
Event Loop Interoperability,事件循环操作
有很多说,没有选择JavaScript作为开发语言,会严重影响Flutter的推广。毕竟JavaScript如此普遍,相对于新学一门语言,开发人员转移到Flutter的学习成本会很低。
我们想到的,人家Flutter团队不可能想不到。那为什么Flutter还是选择了Dart?想知道为什么,还得深挖一下Dart的历史。
Dart历史:
Dart是Google在2011推出了一款应用Web开发的语言,它的团队由Chrome浏览器V8引擎团队的Lars Bak主持,它的推出目的是取代JavaScript做前端开发,成为下一代结构化Web开发语言。Dart提供两种方式运行:一是运行在本地虚拟机上。二是将代码转换成JavaScript。
然而由于Node.js的火爆将JavaScript推到了前后端通吃的地位,Dart也就慢慢退出了开发者的视野,直到改变思路应用于移动端开发后,焕发了第二春。
那Flutter团队为什么没有选择开发者群体更多的JavaScript语音,或者使用C++语言来达到更简便的框架结构,因为用C++直接可以调用Skia,可以直接与原生代码通信(不再需要Dart桥接到C/C++,C/C++再JNI桥接到Java)。
我觉得原因很简单:
Hybird(Html + JsBridge)Cordova
完全利用Web标准,依赖WebView的渲染能力,完全能满足产品或设计对页面实现要求。
为了能访问原生Api,需要原生配合开发一套桥接的接口,暴露出原生系统接口能力。
缺点也很明白,WebView是一个重量级组件,并且Html/Js/css的加载、解析、渲染过程非常耗时。
当然我们可以做成离线方式,将Html页面提前下载到本地,这样会提高加载的速度。但是由于WebView对Html页面的解析、渲染都是在一个线程中执行的,对于复杂的交互和页面动画实现上,效果差强人意。
ReactNative Weex 小程序 快应用
虽然还是使用JavaScript语言基于WebView开发,但是自定义一套控件标签,并一一映射到原生控件,将页面渲染委托给原生系统,所以渲染速度上会优于纯Web方式。而且单独使用js引擎自定义了js的解析,例如iOS的jscore、小程序用腾讯自己的X5浏览器内核,快应用用阿里的UC浏览器内核。
映射到原生控件的方式显示比WebView渲染能力高一些,所以这些方案在发布的时候就标榜自己能达到原生应用的用户体验。但是这种方式有两个缺点:第一个还是构建复杂交互时要依赖映射方案中提供的接口,如果不满足还是要原生开发配合来一对一挖细节来实现,甚至直接由原生页面实现。第二个是系统升级后,会有新的原生组件出现,或者旧的组件弃用,所以系统适配的难度较大。
Xamarin C#实现
Flutter
基于高效的2D渲染引擎Skia,完全独立的UI绘制渲染逻辑,不依赖WebView,也不依赖原生控件,能完全解决前面的跨平台方案的最大短板。
当然,如果需要访问系统Api的话,还得额外需要插件开发,将Dart与原生语言建立通信渠道。这是每个跨平台方案都躲不过的一道坎,那么如果系统的UI开发框架就是Flutter呢?例如Fuchsia, 那就不需要插件开发了。
那么Flutter就没有缺点了么?
1、安装包体积过大。相对原生开发出来的安装包,Flutter应用安装包大很多,一个Demo示例都要10M左右的体积。因为要把Flutter运行环境、Skia绘图引擎、还有其他一些支持库打包进apk、ipa中。
2、不能动态化部署,因为不像JavaScript是解释执行的,在正式发布时,Dart是完全编译成机器码的,并且不支持反射。
3、调用系统Api时需要额外的通信,所以调用效率肯定比原生差一些
4、目前的混合开发功能还处于预览状态,如果需要混合开发需要自己搞一套机制(原生页面与Flutter页面栈管理、多个Flutter环境的资源使用问题)出来,闲鱼团队已经做了一些努力。
我们通过Flutter可以构建出媲美原生的跨平台应用,其基本原理就是重写UI绘制逻辑,通过高效的2D绘制引擎Skia实现页面的快速渲染。同时提供了开发友好的JIT模式,提高了开发效率。