Flutter混编实践
一、跨平台技术的出现背景
1. 原生开发
Android基于Java或者Kotlin, iOS基于Objective-C或Swift,直接调用各自平台的SDK开发的应用程序;
- 优点:能快速访问各种硬件功能(GPS、摄像头)速度快、性能高、可以实现复杂动画及绘制,整体用户体验好
- 缺点: 开发成本高,一个应用要维护两套代码,一致性差
为了加快产品的开发周期,提高开发效率,保持多端的一致性,逐渐出现了多种跨平台技术,下面选取一些常用技术作为对比:
2. H5+原生混合开发
H5技术在各类app中使用的频率非常高,通过原生的网页加载控件WebView (Android)或WKWebView(iOS)来加载H5页面,而WebView实质上就是一个浏览器内核,其JavaScript依然运行在一个权限受限的沙箱中,所以对于大多数系统能力都没有访问权限,如无法访问文件系统、不能使用蓝牙等。所以我们通常要使用JsBridge来进行两端的通信,虽然web技术栈社区及资源丰富,但是性能不够好,对于复杂用户界面或动画,容易出现卡顿和掉帧。
3. React Native
React Native (简称RN)是Facebook于2015年4月开源的跨平台移动应用开发框架,是Facebook早先开源的JS框架 React 在原生移动应用平台的衍生产物,目前支持iOS和Android两个平台。RN使用Javascript语言,类似于HTML的JSX,以及CSS来开发移动应用。
4. Weex
Weex是阿里巴巴于2016年发布的跨平台移动端开发框架,思想及原理和React Native类似,最大的不同是语法层面,Weex支持Vue语法和Rax语法,Rax 的 DSL(Domain Specific Language) 语法是基于 React JSX 语法而创造。与 React 不同,在 Rax 中 JSX 是必选的,它不支持通过其它方式创建组件,所以学习 JSX 是使用 Rax 的必要基础。而React Native只支持JSX语法。
二 、Flutter的优势
Flutter和以上几种跨平台技术最大的不同是其自行实现一套渲染框架,可通过调用skia等方式完成自渲染,而不依赖于原生控件,也不用依赖于webview,使用的语言也从JavaScript变成了Dart。
1. 自绘引擎Skia
Flutter使用Skia作为其2D渲染引擎,Skia是Google的一个2D图形处理函数库,包含字型、坐标转换,以及点阵图都有高效能且简洁的表现,Skia是跨平台的,并提供了非常友好的API,目前Google Chrome浏览器和Android均采用Skia作为其绘图引擎。
2. Dart语言
Flutter使用Dart而不是JavaScript作为开发语言,主要有如下考虑:
- 基于JIT的快速开发周期:Flutter在开发阶段采用,采用JIT模式,这样就避免了每次改动都要进行编译,极大的节省了开发时间;
- 基于AOT的发布包: Flutter在发布时可以通过AOT生成高效的ARM代码以保证应用性能。而JavaScript则不具有这个能力。
3. 总结:
技术类型 | UI渲染方式 | 性能 | 开发效率 | 动态化 | 框架代表 |
---|---|---|---|---|---|
H5 | WebView 渲染 | 一般 | 高 | 支持 | Cordova、Ionic |
JavaScript+原生渲染 | 好 | 中 | 中 | 支持 | RN、Weex |
Flutter | 好 | 高 | 高 | 不支持 | Flutter |
三、Flutter框架结构
Flutter架构Flutter 架构采用分层设计,从下到上分为三层,依次为:Embedder、Engine、Framework。
1. Embedder
操作系统适配层,实现了渲染 Surface 设置,线程设置,以及平台插件等平台相关特性的适配。从这里我们可以看到,Flutter 平台相关特性并不多,这就使得从框架层面保持跨端一致性的成本相对较低。
2. Engine
主要包含 Skia、Dart 和 Text,实现了 Flutter 的渲染引擎、文字排版、事件处理和 Dart 运行时等功能。Skia 和 Text 为上层接口提供了调用底层渲染和排版的能力,Dart 则为 Flutter 提供了运行时调用 Dart 和渲染引擎的能力。而 Engine 层的作用,则是将它们组合起来,从它们生成的数据中实现视图渲染。
3. Framework
层则是一个用 Dart 实现的 UI SDK,包含了动画、图形绘制和手势识别等功能。为了在绘制控件等固定样式的图形时提供更直观、更方便的接口,Flutter 还基于这些基础能力,根据 Material 和 Cupertino 两种视觉设计风格封装了一套 UI 组件库。我们在开发 Flutter 的时候,可以直接使用这些组件库。
通过上面的架构可以看出,底层使用Skia引擎来统一渲染,上层开发接口和功能体验也就随即统一了,开发者再也不用操心平台相关的渲染特性了。也就是说,Skia 保证了同一套代码调用在 Android 和 iOS 平台上的渲染效果是完全一致的。
四、Flutter是如何实现跨平台的?
1. 自绘UI
引擎基于 Skia 绘制,操作 OpenGL、GPU,不需要依赖原生的组件渲染框架。
Flutter绘制过程
Layer Tree:这个是dart runtime输出的一个树状数据结构,树上的每一个叶子节点,代表了一个界面元素(Button,Image等等)。
Skia:这个是谷歌的一个跨平台渲染框架,从目前IOS和anrdroid来看,SKIA底层最终都是调用OpenGL绘制。Vulkan支持还不太好,Metal还不支持。
Shell:这里的Shell特指平台特性(Platform)的那一部分,包含IOS和Android平台相关的实现,包括EAGLContext管理、上屏的操作以及后面将会重点介绍的外接纹理实现等等。
从图中可以看出,当Runtime完成Layout输出一个Layertree以后,在管线中会遍历Layertree的每一个叶子节点,每一个叶子节点最终会调用Skia引擎完成界面元素的绘制,在遍历完成后,在调用glPresentRenderBuffer(IOS)或者glSwapBuffer(Android)按完成上屏操作。
在Android中的层级关系图如下:
关键类层级关系
- SurfaceView
- TextureView
2. PlatformChannel
在实际的开发过程中,我们时常要从flutter端访问native的数据或者硬件能力,这个时候需要使用到Platform Channel进行通信
channel示意图五、混合框架的搭建
对于这种新型技术,为了降低引入的风险,一般都会逐步地迁移和体验,这样就涉及到混合项目的搭建,native和flutter两端的页面交互和数据同步等问题
1、混合栈管理
为了实现高效、便捷的栈管理和页面交互,我们引入了闲鱼开源的FlutterBoost,它主要为我们解决了以下一些痛点:
- 统一了native和flutter之间跳转方式
- 提供与native一致的生命周期管理
- 优化FlutterEngine的使用,减少内存消耗
- 其他(比如黑屏闪屏的坑)
FlutterBoost和其他常规方案对比如下:
路由方案对比
2、自定义插件封装
混合项目绕不开两端的数据交互,于是我们基于MethodChannel封装了一些常用的插件,主要用来打通native和flutter两端的数据同步和硬件访问能力
3. 整体架构
我们使用google官方的方式,将Flutter模块作为一个module,让native那边依赖,形成了下的架构
整体架构
六、仓库和依赖管理
为了方便Android和iOS两端能够同步地访问代码,我们将Flutter模块单独抽取到一个仓库,然后提供给native去依赖
1. 仓库地址
- Flutter模块仓库地址:http://git.fcbox.com/WA/ANG/flutter/Honey-Flutter
- iOS丰蜜项目仓库地址:http://git.fcbox.com/WA/iOS/Hiveoperation.git
- Android丰蜜项目仓库地址:http://git.fcbox.com/WA/ANG/Honey
2. 依赖关系
仓库依赖.png七、落地效果
丰蜜项目中已经实现了“个人中心”模块的Flutter重构工作,并且成功地接入到Android和iOS两端,目前看来效果很理想,release包的体验几乎和原生无异,主要改造的界面如下:
1. Android端效果图
Android端效果图2. iOS端效果图
iOS端效果图3. 动态效果
ezgif.com-video-to-gif (1).gif八、总结
1. 收益
使用Flutter混编,大概有80%以上的代码能够公用,剩余一小部分需要两端单独处理,总的来说Flutter在两个平台上的UI一致性是非常好的,帧率也比较稳定,后面推广开来的话,会对APP端的开发效率有一个大幅提升
2. 展望
- 将Flutter部分的基础和公共代码封装成框架,方便其他项目引入
- Flutter官方目前也在适配Web端,时机成熟,甚至可以一套代码3端公用
- 动态化方案研究
- 包体大小优化( 目前丰蜜的relaese包在Android端增加8M左右,iOS端压增加15M左右)
3. 上手难度
移动端的开发思维其实是相通的,Dart语言对于Android和iOS端的同学来说还是很好上手的,语法有点Java和Kotlin、Swift的影子。Flutter的设计思想也是受React启发,一切都是Widget,没有像android ios 那些些activity fragment 杂七杂八的概念,所以Web端的同学学习起来也更容易理解