姬長信(Redy)

Phenomenally fluid 3D creation.

一直以来/uff0c跨平台开发都是困扰移动客户端开发的难题。 在马蜂窝旅游 App 很多业务场景里/uff0c我们尝试过一些主流的跨平台开发解决方案/uff0c 比如 WebView 和 React Native/uff0c来提升开发效率和用户体验。但这两种方式也带来了新的问题。 比如使用 WebView 跨平台方式/uff0c优点确实非常明显。基于 WebView 的框架集成了当下 Web 开发的诸多优势/uff1a丰富的控件库、动态化、良好的技术社区、测试自动化等等。但是缺点也同样明显/uff1a渲染效率和 JavaScript 的执行能力都比较差/uff0c使页面的加载速度和用户体验都不尽如人意。 而使用以 React Native/uff08简称 RN/uff09为代表的框架时/uff0c维护又成了大难题。RN 使用类 HTML+JS 的 UI 创建逻辑/uff0c生成对应的原生页面/uff0c将页面的渲染工作交给了系统/uff0c所以渲染效率有很大的优势。但由于 RN 代码是通过 JS 桥接的方式转换为原生的控件/uff0c所以受各个系统间的差异影响非常大/uff0c虽然可以开发一套代码/uff0c但对各个平台的适配却非常的繁琐和麻烦。 ## 为什么是 Flutter 2018 年 12 月初/uff0cGoogle 正式发布了开源跨平台 UI 框架 Flutter 1.0 Release 版本/uff0c马蜂窝电商客户端团队进行了调研与实践/uff0c发现 Flutter 能很好的帮助我们解决开发中遇到的问题。 1. **跨平台开发**/uff0c**针对 Android 与 iOS 的风格设计了两套设计语言的控件实现**/uff08Material & Cupertino/uff09。这样不但能够节约人力成本/uff0c而且在用户体验上更好的适配 App 运行的平台。 2. **重写了一套跨平台的 UI 框架/uff0c渲染引擎是依靠 Skia 图形库实现**。Flutter 中的控件树直接由渲染引擎和高性能本地 ARM 代码直接绘制/uff0c不需要通过中间对象/uff08Web 应用中的虚拟 DOM 和真实 DOM/uff0c原生 App 中的虚拟控件和平台控件/uff09来绘制/uff0c使它有接近原生页面的性能/uff0c帮助我们提供更好的用户体验。 3. **同时支持 JIT 和 AOT 编译**。JIT 编译方式使其在开发阶段有个备受欢迎的功能/u2014/u2014热重载/uff08HotReload/uff09/uff0c这样在开发时可以省去构建的过程/uff0c提高开发效率。而在 Release 运行阶段采用 AOT 的编译方式/uff0c使执行效率非常高/uff0c让 Release 版本发挥更好的性能。 于是/uff0c电商客户端团队决定探索 Flutter 在跨平台开发中的新可能/uff0c并率先应用于商家端 App 中。在本文中/uff0c我们将结合 Flutter 在马蜂窝商家端 App 中的应用实践/uff0c探讨 Flutter 架构的实现原理/uff0c有何优势/uff0c以及如何帮助我们解决问题。 ## Flutter 架构和实现原理 Flutter 使用 Dart 语言开发/uff0c主要有以下几点原因/uff1a - Dart 一般情况下是运行 DartVM 上/uff0c但是也可以编译为 ARM 代码直接运行在硬件上。 - Dart 同时支持 AOT 和 JIT 两种编译方式/uff0c可以更好的提高开发以及 App 的执行效率。 - Dart 可以利用独特的隔离区/uff08Isolate/uff09实现多线程。而且不共享内存/uff0c可以实现无锁快速分配。 - 分代垃圾回收/uff0c非常适合 UI 框架中常见的大量 Widgets 对象创建和销毁的优化。 - 在为创建的对象分配内存时/uff0cDart 是在现有的堆上移动指针/uff0c保证内存的增长是程线性的/uff0c于是就省了查找可用内存的过程。 Dart 主要由 Google 负责开发和维护。目前 Dart 最新版本已经是 2.2/uff0c针对 App 和 Web 开发做了很多优化。并且对于大多数的开发者而言/uff0cDart 的学习成本非常低。 Flutter 架构也是采用的分层设计。从下到上依次为/uff1aEmbedder/uff08嵌入器/uff09、Engine、Framework。 ![](https://oscimg.oschina.net/oscnet/a90e774bd6c7dbbdb7bfe8b6f760afd5086.jpg) 图 1: Flutter 分层架构图 **Embedder** 是嵌入层/uff0c做好这一层的适配 Flutter 基本可以嵌入到任何平台上去/uff1b **Engine** 层主要包含 Skia、Dart 和 Text。Skia 是开源的二位图形库/uff1bDart 部分主要包括 runtime、Garbage Collection、编译模式支持等/uff1bText 是文本渲染。**Framework** 在最上层。我们的应用围绕 Framework 层来构建/uff0c因此也是本文要介绍的重点。 ### Framework **1.【Foundation】**在最底层/uff0c主要定义底层工具类和方法/uff0c以提供给其他层使用。 **2.【Animation】**是动画相关的类/uff0c可以基于此创建补间动画/uff08Tween Animation/uff09和物理原理动画/uff08Physics-based Animation/uff09/uff0c类似 Android 的 ValueAnimator 和 iOS 的 Core Animation。 **3.【Painting】**封装了 Flutter Engine 提供的绘制接口/uff0c例如绘制缩放图像、插值生成阴影、绘制盒模型边框等。 **4.【Gesture】**提供处理手势识别和交互的功能。 **5.【Rendering】**是框架中的渲染库。控件的渲染主要包括三个阶段/uff1a布局/uff08Layout/uff09、绘制/uff08Paint/uff09、合成/uff08Composite/uff09。 从下图可以看到/uff0cFlutter 流水线包括 7 个步骤。 ![](https://oscimg.oschina.net/oscnet/461346d6e010d9ef5a5aa07c97b7995b231.jpg) 图 2: Flutter 流水线 首先是获取到用户的操作/uff0c然后你的应用会因此显示一些动画/uff0c接着 Flutter 开始构建 Widget 对象。 Widget 对象构建完成后进入渲染阶段/uff0c这个阶段主要包括三步/uff1a - **布局元素**/uff1a决定页面元素在屏幕上的位置和大小/uff1b - **绘制阶段**/uff1a将页面元素绘制成它们应有的样式/uff1b - **合成阶段**/uff1a按照绘制规则将之前两个步骤的产物组合在一起。 最后的光栅化由 Engine 层来完成。 **在渲染阶段**/uff0c控件树/uff08widget/uff09会转换成对应的渲染对象/uff08RenderObject/uff09树/uff0c在 Rendering 层进行布局和绘制。 在布局时 Flutter 深度优先遍历渲染对象树。数据流的传递方式是从上到下传递约束/uff0c从下到上传递大小。也就是说/uff0c父节点会将自己的约束传递给子节点/uff0c子节点根据接收到的约束来计算自己的大小/uff0c然后将自己的尺寸返回给父节点。整个过程中/uff0c位置信息由父节点来控制/uff0c子节点并不关心自己所在的位置/uff0c而父节点也不关心子节点具体长什么样子。 ![](https://oscimg.oschina.net/oscnet/0e66160ffe165b90232893c1002ae417e33.jpg) 图 3: 数据流传递方式 为了防止因子节点发生变化而导致的整个控件树重绘/uff0cFlutter 加入了一个机制/u2014/u2014**Relayout Boundary**/uff0c在一些特定的情形下 Relayout Boundary 会被自动创建/uff0c不需要开发者手动添加。 例如/uff0c控件被设置了固定大小/uff08tight constraint/uff09、控件忽略所有子视图尺寸对自己的影响、控件自动占满父控件所提供的空间等等。很好理解/uff0c就是控件大小不会影响其他控件时/uff0c就没必要重新布局整个控件树。有了这个机制后/uff0c无论子树发生什么样的变化/uff0c处理范围都只在子树上。 ![](https://oscimg.oschina.net/oscnet/fd5d183f8fe84c618d00b93f07968c5b565.jpg) 图 4: Relayout Boundary 机制 在确定每个空间的位置和大小之后/uff0c就进入**绘制阶段**。绘制节点的时候也是深度遍历绘制节点树/uff0c然后把不同的 RenderObject 绘制到不同的图层上。 这时有可能出现一种特殊情况/uff0c如下图所示节点 2 在绘制子节点 4 时/uff0c由于其节点 4 需要单独绘制到一个图层上/uff08如 video/uff09/uff0c因此绿色图层上面多了个黄色的图层。之后再需要绘制其他内容/uff08标记 5/uff09就需要再增加一个图层/uff08红色/uff09。再接下来要绘制节点 1 的右子树/uff08标记 6/uff09/uff0c也会被绘制到红色图层上。所以如果 2 号节点发生改变就会改变红色图层上的内容/uff0c因此也影响到了毫不相干的 6 号节点。 ![](https://oscimg.oschina.net/oscnet/23b34bb3984f1c8e0ac5cade96112d92cab.jpg) 图 5: 绘制节点与图层的关系 为了避免这种情况/uff0cFlutter 的设计者这里基于 Relayout Boundary 的思想增加了 **Repaint Boundary**。在绘制页面时候如果遇见 Repaint Boundary 就会强制切换图层。 如下图所示/uff0c在从上到下遍历控件树遇到 Repaint Boundary 会重新绘制到新的图层/uff08深蓝色/uff09/uff0c在从下到上返回的时候又遇到 Repaint Boundary/uff0c于是又增加一个新的图层/uff08浅蓝色/uff09。 ![](https://oscimg.oschina.net/oscnet/c801cbf85db8bde16eae1b70d0cb49ef9bb.jpg) 图 6: Repaint Boundary 机制 这样/uff0c即使发生重绘也不会对其他子树产生影响。比如在 Scrollview 上/uff0c当滚动的时候发生内容重绘/uff0c如果在 Scrollview 以外的地方不需要重绘就可以使用 Repaint Boundary。Repaint Boundary 并不会像 Relayout Boundary 一样自动生成/uff0c而是需要我们自己来加入到控件树中。 **6.【Widget】控件层**。所有控件的基类都是 Widget/uff0cWidget 的数据都是只读的, 不能改变。所以每次需要更新页面时都需要重新创建一个新的控件树。每一个 Widget 会通过一个 RenderObjectElement 对应到一个渲染节点/uff08RenderObject/uff09/uff0c可以简单理解为 Widget 中只存储了页面元素的信息/uff0c而真正负责布局、渲染的是 RenderObject。 在页面更新重新生成控件树时/uff0cRenderObjectElement 树会尽量保持重用。由于 RenderObjectElement 持有对应的 RenderObject/uff0c所有 RenderObject 树也会尽可能的被重用。如图所示就是三棵树之间的关系。在这张图里我们把形状当做渲染节点的类型/uff0c颜色是它的属性/uff0c即形状不同就是不同的渲染节点/uff0c而颜色不同只是同一对象的属性的不同。 ![](https://oscimg.oschina.net/oscnet/df1cf2877888fc483f9da162b9226ac5966.jpg) 图 7:  Widget、Element 和 Render 之间的关系 如果想把方形的颜色换成黄色/uff0c将圆形的颜色变成红色/uff0c由于控件是不能被修改的/uff0c需要重新生成两个新的控件 Rectangle yellow 和 Circle red。由于只是修改了颜色属性/uff0c所以 Element 和 RenderObject 都被重用/uff0c而之前的控件树会被释放回收。 ![](https://oscimg.oschina.net/oscnet/306b7ab7c7d4da2cbe537b30a46c78b957a.jpg) 图 8: 示例 那么如果把红色圆形变成三角形又会怎样呢/uff1f由于这里发生变化的是类型/uff0c所以对应的 Element 节点和 RenderObject 节点都需要重新创建。但是由于黄色方形没有发生改变/uff0c所以其对应的 Element 节点和 RenderObject 节点没有发生变化。 ![](https://oscimg.oschina.net/oscnet/6fcf8eba5aeadaf63ee795685dfe061fa03.jpg) 图 9: 示例 **7/. 最后是【Material】 & 【Cupertino**】/uff0c这是在 Widget 层之上框架为开发者提供的基于两套设计语言实现的 UI 控件/uff0c可以帮助我们的 App 在不同平台上提供接近原生的用户体验。 ## Flutter 在马蜂窝商家端App 中的应用实践 ## ![](https://oscimg.oschina.net/oscnet/44681ee2cf792fa0aace5c1bb7be2f6e049.jpg)![](https://oscimg.oschina.net/oscnet/98077a649e7278f893717cd298a92587f5b.jpg) 图 10: 马蜂窝商家端使用 Flutter 开发的页面 ### 开发方式/uff1aFlutter + Native 由于商家端已经是一款成熟的 App/uff0c不可能创建一个新的 Flutter 工程全部重新开发/uff0c因此我们选择 Native 与 Flutter 混编的方案来实现。 在了解 Native 与 Flutter 混编方案前/uff0c首先我们需要了解在 Flutter 工程中/uff0c通常有以下 4 种工程类型/uff1a **1/. Flutter Application** 标准的 Flutter App 工程/uff0c包含标准的 Dart 层与 Native 平台层。 **2/. Flutter Module** Flutter 组件工程/uff0c仅包含 Dart 层实现/uff0cNative 平台层子工程为通过 Flutter 自动生成的隐藏工程/uff08.ios  / .android/uff09。 **3/. Flutter Plugin** Flutter 平台插件工程/uff0c包含 Dart 层与 Native 平台层的实现。 **4/. Flutter Package** Flutter 纯 Dart 插件工程/uff0c仅包含 Dart 层的实现/uff0c往往定义一些公共 Widget。 了解了 Flutter 工程类型后/uff0c我们来看下官方提供的一种混编方案/uff08[https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps](https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps) )/uff0c即在现有工程下创建 Flutter Module 工程/uff0c以本地依赖的方式集成到现有的 Native 工程中。 **官方集成方案/uff08以 iOS 为例/uff09** a. 在工程目录创建 FlutterModule/uff0c创建后/uff0c工程目录大致如下/uff1a ![](https://oscimg.oschina.net/oscnet/6ee0309f08b1fe8c90b0ea834c770b10815.jpg) b. 在 Podfile 文件中添加以下代码/uff1a ``` flutter_application_path = '../flutter_Moudule/' ``` 该脚本主要负责/uff1a - pod 引入 Flutter.Framework 以及 FlutterPluginRegistrant 注册入口 - pod 引入 Flutter 第三方 plugin - 在每一个 pod 库的配置文件中写入对 Generated.xcconfig 文件的导入 - 修改 pod 库的 ENABLE_BITCODE = NO/uff08因为 Flutter 现在不支持 bitcode/uff09 c. 在 iOS 构建阶段 Build Phases 中注入构建时需要执行的 xcode/_backend.sh (位于 FlutterSDK/packages/flutter/_tools/bin) 脚本: ``` "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build ``` 该脚本主要负责/uff1a - 构建 App.framework 以及 Flutter.framework 产物 - 根据编译模式/uff08debug/profile/release/uff09导入对应的产物 - 编译 flutter_asset 资源 - 把以上产物 copy 到对应的构建产物中 d. 与 Native 通信 - **方案一**/uff1a改造 AppDelegate 继承自 FlutterAppDelegate - **方案二**/uff1aAppDelegate 实现 FlutterAppLifeCycleProvider 协议/uff0c生命周期由 FlutterPluginAppLifeCycleDelegate 传递给 Flutter 以上就是官方提供的集成方案。我们最终没有选择此方案的原因/uff0c是它直接依赖于 FlutterModule 工程以及 Flutter 环境/uff0c使 Native 开发同学无法脱离 Flutter 环境开发/uff0c影响正常的开发流程/uff0c团队合作成本较大/uff1b而且会影响正常的打包流程。/uff08目前 Flutter 团队正在重构嵌入 Native 工程的方式/uff09 最终我们选择另一种方案来解决以上的问题/uff1a**远端依赖产物。** ![](https://oscimg.oschina.net/oscnet/0fb753c0f6a1a7c81dd6ba320b21a0b5e92.jpg) 图 11 /uff1a远端依赖产物 #### iOS 集成方案 通过对官方混编方案的研究/uff0c我们了解到 iOS 工程最终依赖的其实是 FlutterModule 工程构建出的产物/uff08Framework/uff0cAsset/uff0cPlugin/uff09/uff0c只需将产物导出并 push 到远端仓库/uff0ciOS 工程通过远端依赖产物即可。 依赖产物目录结构如下/uff1a ![](https://oscimg.oschina.net/oscnet/102b9d7cacc48e35c9ed2349bcd0bf3559d.jpg) - **App.framework** : Flutter 工程产物/uff08包含 Flutter 工程的代码/uff0cDebug 模式下它是个空壳/uff0c代码在 flutter_assets 中/uff09。 - **Flutter.framework:** Flutter 引擎库。与编译模式/uff08debug/profile/release/uff09以及 CPU 架构/uff08arm*, i386, x86_64/uff09相匹配。 - **lib.a & .h 头文件:** FlutterPlugin 静态库/uff08包含在 iOS 端的实现/uff09。 - **flutter_assets**: 包含 Flutter 工程字体/uff0c图片等资源。在 Flutter1.2 版本中/uff0c被打包到 App.framework 中。 #### Android 集成方案 Android Nativite 集成是通过 Gradle 远程依赖 Flutter 工程产物的方式完成的/uff0c以下是具体的集成流程。 a.创建 Flutter 标准工程 ``` $ flutter create flutter_demo ``` 默认使用 Java 代码/uff0c如果增加 Kotlin 支持/uff0c使用如下命令/uff1a ``` $ flutter create -a kotlin flutter_demo ``` b. 修改工程的默认配置 ![](https://oscimg.oschina.net/oscnet/5e6c2f683388647a362964a23201b1206fc.jpg) 1. 修改 app module 工程的 build.gradle 配置  apply plugin: 'com.android.application' => apply plugin: 'com.android.library'/uff0c并移除 applicationId 配置 2. 修改 root 工程的 build.gradle 配置 _在集成过程中 Flutter 依赖了三方 Plugins 后/uff0c遇到 Plugins 的代码没有被打进 Library 中的问题。通过以下配置解决/uff08这种方式略显粗暴/uff0c后续的优化方案正在调研/uff09。_ ``` subprojects { project.buildDir = "${rootProject.buildDir}/app" } ``` 1. app module 增加 maven 打包配置 2. c. 生成 Android Flutter 产物 ``` $ cd android $ ./gradlew uploadArchives ``` _官方默认的构建脚本在 Flutter 1.0.0 版本存在 Bug/u2014/u2014最终的产物中会缺少 flutter_shared/icudtl.dat 文件/uff0c导致 App Crash。目前的解决方式是将这个文件复制到工程的 assets 下/uff08 在 Flutter 最新 1.2.1 版本中这个 Bug 已被修复/uff0c但是 1.2.1 版本又出现了一个 UI 渲染的问题/uff0c所以只能继续使用 1.0.0 版本/uff09。_ d. Android Native 平台工程集成/uff0c增加下面依赖配置即可/uff0c不会影响 Native 平台开发的同学。 ``` implementation 'com.mfw.app:MerchantFlutter:0.0.5-beta' ``` ### Flutter 和 iOS、Android  的交互 使用平台通道/uff08Platform Channels/uff09在 Flutter 工程和宿主/uff08Native 工程/uff09之间传递消息/uff0c主要是通过 MethodChannel 进行方法的调用/uff0c如下图所示/uff1a ![](https://oscimg.oschina.net/oscnet/429dd95d188f44af13f9f82273ec8965a06.jpg) 图 12 /uff1aFlutter 与 iOS、Android 交互 为了确保用户界面不会挂起/uff0c消息和响应是异步传递的/uff0c需要用 async 修饰方法/uff0cawait 修饰调用语句。Flutter 工程和宿主工程通过在 Channel 构造函数中传递 Channel 名称进行关联。单个应用中使用的所有 Channel 名称必须是唯一的; 可以在 Channel 名称前加一个唯一的「域名前缀」。 ### Flutter 与  Native 性能对比 我们分别使用 Native 和 Flutter 开发了两个列表页/uff0c以下是页面效果和性能对比/uff1a #### **iOS 对比**/uff08机型 6P 系统 10.3.3/uff09/uff1a Flutter 页面/uff1a ![](https://oscimg.oschina.net/oscnet/a8a709329c10aa400d95862acee674149d1.jpg)![](https://oscimg.oschina.net/oscnet/9a74563bf85749a0ec56537a9cabe21ff5e.jpg) iOS Native 页面/uff1a ![](https://oscimg.oschina.net/oscnet/871179200eda2df214f0b080cf24eaf438a.jpg)![](https://oscimg.oschina.net/oscnet/7e5762f62ab2d5e54fa9e7afe9e0928fe37.jpg) 可以看到/uff0c从使用和直观感受都没有太大的差别。于是我们采集了一些其他方面的数据。 Flutter 页面/uff1a ![](https://oscimg.oschina.net/oscnet/a86ef50c88830b5cfe7e3f90b0815efe8a5.jpg) ![](https://oscimg.oschina.net/oscnet/b21d90082a4e51620aff4831ca85ff4fa74.jpg) iOS Native 页面/uff1a ![](https://oscimg.oschina.net/oscnet/b77c8918bcf856bf8626dcc09a7ec111dbe.jpg) ![](https://oscimg.oschina.net/oscnet/4ee214a66d8faed36dabd7a62ec45bae197.jpg) ![](https://oscimg.oschina.net/oscnet/859d7726573e1acd7ac1679d49df532da11.jpg) 另外我们还对比了商家端接入 Flutter 前后包体积的大小/uff1a39Mb /u2192  44MB ![](https://oscimg.oschina.net/oscnet/8910afe947bc2fa8b8a9c57fcbbb943eca5.jpg) 在 iOS 机型上/uff0c流畅度上没有什么差异。从数值上来看/uff0cFlutter 在 内存跟 GPU/CPU 使用率上比原生略高。Demo 中并没有对 Flutter 做更多的优化/uff0c可以看出 Flutter 整体来说还是可以做出接近于原生的页面。 **下面是 Flutter 与 Android 的性能对比。** Flutter 页面/uff1a ![](https://oscimg.oschina.net/oscnet/99ef8a9a0d99ddf9ab3cee7042a06026577.jpg) Android Native 页面/uff1a ![](https://oscimg.oschina.net/oscnet/ccb0cb4293f81e2bc4948350ce3ea0cf26c.jpg) 从以上两张对比图可以看出/uff0c不考虑其他因素/uff0c单纯从性能角度来说/uff0c原生要优于 Flutter/uff0c但是差距并不大/uff0c而且 Flutter 具有的跨平台开发和热重载等特点极大地节省了开发效率。并且/uff0c未来的热修复特性更是值得期待。 ## 混合栈管理 首先先介绍下 Flutter 路由的管理/uff1a - Flutter 管理页面有两个概念/uff1aRoute 和 Navigator。 - Navigator 是一个路由管理的 Widget/uff08Flutter 中万物皆 Widget/uff09/uff0c它通过一个栈来管理一个路由 Widget 集合。通常当前屏幕显示的页面就是栈顶的路由。 - 路由 (Route) 在移动开发中通常指页面/uff08Page/uff09/uff0c这跟 web 开发中单页应用的 Route 概念意义是相同的/uff0cRoute 在 Android 中通常指一个 Activity/uff0c在 iOS 中指一个 ViewController。所谓路由管理/uff0c就是管理页面之间如何跳转/uff0c通常也可被称为导航管理。这和原生开发类似/uff0c无论是 Android 还是 iOS/uff0c导航管理都会维护一个路由栈/uff0c路由入栈 (push) 操作对应打开一个新页面/uff0c路由出栈 (pop) 操作对应页面关闭操作/uff0c而路由管理主要是指如何来管理路由栈。 ![](https://oscimg.oschina.net/oscnet/7a493b112528b02514182e146a50d9efe09.jpg) 图 14 /uff1aFlutter 路由管理 如果是纯 Flutter 工程/uff0c页面栈无需我们进行管理/uff0c但是引入到 Native 工程内/uff0c就需要考虑如何管理混合栈。并且需要解决以下几个问题/uff1a 1/. 保证 Flutter 页面与 Native 页面之间的跳转从用户体验上没有任何差异 2/. 页面资源化/uff08马蜂窝特有的业务逻辑/uff09 3/. 保证生命周期完整性/uff0c处理相关打点事件上报 4/. 资源性能问题 参考了业界内的解决方法/uff0c以及项目自身的实际场景/uff0c我们选择类似于 H5 在 Navite 中嵌入的方式/uff0c统一通过 openURL 跳转到一个 Native 页面/uff08FlutterContainerVC/uff09/uff0cNative 页面通过 addChildViewController 方式添加 FlutterViewController/uff08负责 Flutter 页面渲染/uff09/uff0c同时通过 channel 同步 Native 页面与 Flutter 页面。 - 每一次的 push/pop 由 Native 发起/uff0c同时通过 channel 保持 Native 与 Flutter 页面同步/u2014/u2014在 Native 中跳转 Flutter 页面与跳转原生无差异 - 一个 Flutter 页面对应一个 Native 页面/uff08FlutterContainerVC)/u2014/u2014解决页面资源化 - FlutterContainerVC 通过 addChildViewController 对单例 FlutterViewController 进行复用/u2014/u2014保证生命周期完整性/uff0c处理相关打点事件上报 - 由于每一个 FlutterViewController/uff08提供 Flutter 视图的实现/uff09会启动三个线程/uff0c分别是 UI 线程、GPU 线程和 IO 线程/uff0c使用单例 FlutterViewController 可以减少对资源的占用/u2014/u2014解决资源性能问题 ## Flutter 应用总结 Flutter 一经发布就很受关注/uff0c除了 iOS 和 Android 的开发者/uff0c很多前端工程师也都非常看好 Flutter 未来的发展前景。相信也有很多公司的团队已经投入到研究和实践中了。不过 Flutter 也有很多不足的地方/uff0c值得我们注意/uff1a 1. 虽然 1.2 版本已经发布/uff0c但是目前没有达到完全稳定状态/uff0c1.2 发布完了就出现了控件渲染的问题。加上 Dart 语言生态小/uff0c学习资料可能不够丰富。 2. 关于动态化的支持/uff0c目前 Flutter 还不支持线上动态性。如果要在 Android 上实现动态性相对容易些/uff0ciOS 由于审核原因要实现动态性可能成本很高。 3. Flutter 中目前拿来就用的能力只有 UI 控件和 Dart 本身提供能力/uff0c对于平台级别的能力还需要通过 channel 的方式来扩展。 4. 已有工程迁移比较复杂/uff0c以前沉淀的 UI 控件/uff0c需要重新再实现一套。 5. 最后一点比较有争议/uff0cFlutter 不会从程序中拆分出额外的模板或布局语言/uff0c如 JSX 或 XM L/uff0c也不需要单独的可视布局工具。有的人认为配合 HotReload 功能使用非常方便/uff0c但我们发现这样代码会有非常多的嵌套/uff0c阅读起来有些吃力。 目前阿里的闲鱼开发团队已经将 Flutter 用于大型实践/uff0c并应用在了比较重要的场景/uff08如产品详情页/uff09/uff0c为后来者提供了良好的借鉴。马蜂窝的移动客户端团队关于 Flutter 的探索才刚刚起步/uff0c前面还有很多的问题需要我们一点一点去解决。不过无论从 Google 对其的重视程度/uff0c还是我们从实践中看到的这些优点/uff0c都让我们对 Flutter 充满信心/uff0c也希望在未来我们可以利用它创造更多的价值和奇迹。 路途虽远/uff0c犹可期许。 **本文作者**/uff1a马蜂窝电商研发客户端团队。 **/uff08马蜂窝技术原创内容/uff0c转载务必注明出处保存文末二维码图片/uff0c谢谢配合。/uff09** **参考文献/uff1a** - **Flutter's Layered Design** [https://www.youtube.com/watch?v=dkyY9WCGMi0 ](https://www.youtube.com/watch?v=dkyY9WCGMi0) - **Flutter's Rendering Pipeline** [https://www.youtube.com/watch?v=UUfXWzp0-DU&t=1955s ](https://www.youtube.com/watch?v=UUfXWzp0-DU&t=1955s) - **Flutter 原理与美团的实践** [https://juejin.im/post/5b6d59476fb9a04fe91aa778#comment ](https://juejin.im/post/5b6d59476fb9a04fe91aa778#comment) **关注马蜂窝技术/uff0c找到更多你想要的内容** ![](https://oscimg.oschina.net/oscnet/4edcc462d526cd96005a06758619f3b8a88.jpg)