• frc 68b55478644342ebc9753a7e34dfe7f6 - Flutter 开发 (2)优雅的 Flutter 组件化 混编方案

背景

此篇文章,主要针对想要在原有Native工程的基础上集成Flutter的需求,所提供的混编方案的探讨。

Flutter 官方已经给出了混编方案:https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps#ios 这种方案有 优点,也有 缺点

1. 官方方案的优缺点

(1)优点:
  • 不需要每次 Run 起来之后,先进行 同步flutter代码(组件化Flutter后,因为组件化后flutter代码已经变为framework,所以每次进来需要先热更新同步代码)
  • 不需要单独搞一个组件进行集成,管理组件的版本,发布等。
(2)缺点:
  • 会非常耦合工程,需要修改工程配置,添加 BUILD PHASE 调用 flutter 中 xcode_backend.sh 脚本 去编译 Flutter。

  • 如果使用pod管理,那么还需修改xcconfig配置。

  • 因为需要调用 Flutter 的编译脚本,所以这种方式集成后,团队内所有组员电脑和打包机,都必须安装Flutter环境才能编译成功。

2. Flutter 组件化混编方案

(1)优点:
  • 不需修改 原有 xcconfig 配置。
  • 不需要添加 Run Script 脚本。
  • 运行不需要依赖 Flutter 环境。
(2)缺点
  • 需要单独管理一个 flutter私有索引库。
  • 开发加载 Flutter 页面 首次需要热更新 进行刷新同步 Flutter 代码。
(3)混编方案 实现核心思想
  • 通过查看 Flutter 编译脚本xcode_backend.sh 和 测试单独引入编译产物,发现其实 只要拥有 Flutter 的编译产物,项目就可以接入 Flutter 的功能。
  • 所以说只要把Flutter编译好的产物,放在工程里,那么就无需配置每次调用 xcode_backend.sh 脚本,也无需强耦合Flutter环境,不需要所有组员安装Flutter环境,只需要有发布开发需求的同学进行安装即可。
  • 这就是Flutter组件化的实现核心点。
(4)Flutter 核心编译产物
  • App.framework:dart业务源码相关文件,在 Debug 模式下就是一个很小的空壳,在 Release 模式下包含全部业务逻辑。
  • flutter_assets:Flutter依赖的静态资源,如字体,图片等。
  • Flutter.framework:Flutter库和引擎。

目录

  • Flutter组件化 – 混编方案
  • Flutter组件化 – 断点调试
  • Flutter组件化 – 发布更新
  • Flutter组件化 – 一些坑点

一、Flutter组件化 – 混编方案

  • frc 764d5664fbbb26859eda4166d38464f3 - Flutter 开发 (2)优雅的 Flutter 组件化 混编方案
1. Git仓库存放 – 示例说明

主要分为3个仓库,分别存放Native项目、Flutter 工程源码、Flutter 编译产物私有pod库。
flutter 工程创建,使用 flutter create -t module my_flutter 命令

  • frc 4778b22e3c83b3be5d9adc3972d92c1a - Flutter 开发 (2)优雅的 Flutter 组件化 混编方案
2. 项目目录 – 示例
  • Flutter_iOS :iOS开发主项目。

  • flutter_library :Flutter 项目的开发源码。

  • FlutterSDK :Flutter 源码的编译产物,所构建的私有 pod 库。

  • frc f5270ace4a223a1a0340c4be98874fdb - Flutter 开发 (2)优雅的 Flutter 组件化 混编方案
3. 混编方案说明
  • 根据 只要拥有 Flutter 的编译产物,项目就可以接入 Flutter 的功能 的核心思想,我们如果进行组件化Flutter混编,那么大概思路是:
有组件化环境 – 混编方案说明

(1)在 flutter 项目目录下,执行 flutter build ios 针对 Flutter 项目进行编译打包,生成 Flutter 编译产物。

  • frc 3821196a032b7e2b5567c2a7eb623ea5 - Flutter 开发 (2)优雅的 Flutter 组件化 混编方案

Flutter 的产物分为两种模式,一个是 Debug 模式,采用 JIT(Just In Time)的方式,好处是可以支持热更新,方便调试,,但是性能比较慢。
另一种是 AOT(Ahead Of Time)release 模式,好处是性能比较好。

通过 flutter build ios --debug 可打包出 Debug 下的 Flutter 编译产物。

flutter build ios 命令依赖于 Flutter 生成的 Runner 工程,所以要确保 Runner 工程能够编译成功,这样才能生成 flutter 编译产物。如果遇到编译失败,可以检查下 bundle id 修改一下,使用自己的证书。如下图示例:

  • frc e5eb69dc5b5388044988f31206e81ade - Flutter 开发 (2)优雅的 Flutter 组件化 混编方案

(2)针对编译产物,制作 Flutter SDK 私有库, podspec 指定 App.framework、engine、flutter_assets 路径。

# podspec 有省略
Pod::Spec.new do |s|
  s.name         = "FlutterSDK"
  s.vendored_frameworks = 'Framework/*.framework', 'Framework/engine/*.framework'
  s.resources = 'Framework/flutter_assets'
end

(3)上传 Flutter SDK 私有库项目到云端私有pod索引库。(如何制作私有 pod 索引库,可搜索查看相关资料,这里不细说了)

  • frc bb9edfe446aa86a2b722208bac39e379 - Flutter 开发 (2)优雅的 Flutter 组件化 混编方案

(4)iOS 主项目指定 Podfile ,拉取云端Flutter私有库到本地。

  • frc c7628d5574798a8491a23075c567532e - Flutter 开发 (2)优雅的 Flutter 组件化 混编方案
没有组件化环境 – 混编方案说明
  • 没有组件化环境的项目,并且不会建立私有索引库。
  • 那么只有手动 执行 flutter build ios 命令后,将编译产物手动拖拽到iOS项目中。
4. 最后效果

如下图,可以看到最终工程只引用了一个私有 pod 库。

  • frc 4fe99f85d6742f827fa91312adca0701 - Flutter 开发 (2)优雅的 Flutter 组件化 混编方案

总结

  • 对 flutter 项目执行 flutter build ios 命令,生成编译产物。
  • 针对编译产物,制作为私有 pod 库。
  • 主工程通过 cocoapods 引入私有 pod 库。

二、Flutter 组件化 – 断点调试

因为是编译后的资源接入,我们还需要保证 Flutter 开发的同学可以正常调试。

目录

  • 单独运行 Flutter 工程调试
  • 同时调试 iOS 和 Flutter(不支持断点)
  • 同时调试 iOS 和 Flutter(支持断点)
注意点:
  • 确保,已经安装 Android Studio(用于打开 Flutter 工程)
  • 确保,项目中依赖的 Flutter 打包出来的是 Debug 版本,Release 版本是无法热更新和调试的(使用 flutter build ios –debug 打包 debug 版本)
1. 单独运行 Flutter 工程调试 (只适合和 Native 没有太多关联的工程,比较少用)

使用 Android Studio 打开 Flutter 工程目录

  • frc c0ca99af772a5bdbf1290adf7afa3383 - Flutter 开发 (2)优雅的 Flutter 组件化 混编方案

选择好真机或者模拟器,然后点击 Run 按钮

  • frc 0022d314f3e7ab98a38955382be30de5 - Flutter 开发 (2)优雅的 Flutter 组件化 混编方案

这样 Run 起来后,我们就可以在 Flutter 项目中打断点调试。这种方法 只适合和 Native 没有关联的工程,比较少用。

2. 同时调试 iOS 和 Flutter(不支持Flutter断点的方式)

这种方法,需要先打开 Xcode 运行到 Flutter 页面,再进行附加 Flutter 端口号。

  • 使用 Xcode 打开 iOS 项目,运行起 Flutter 页面。

  • frc d3a86dcae5dba7296457570479405109 - Flutter 开发 (2)优雅的 Flutter 组件化 混编方案
  • 会发现会输出一行日志,其中有一个端口号我们记录下来,例如:

flutter: Observatory listening on http://127.0.0.1:60455/
  • 然后 使用 Android Studio 打开 Flutter 项目(不点击运行),在 底部的 终端框中输入 flutter attach –debug-port=60455,端口号替换为xcode 日志输出的端口号。
  • frc 018044a4fa432d9af5f6858965086947 - Flutter 开发 (2)优雅的 Flutter 组件化 混编方案

Gif 演示

  • frc a253bc06d8225ff193eb01cb89bf6015 - Flutter 开发 (2)优雅的 Flutter 组件化 混编方案

总结

  • 先打开 iOS 工程,运行起 Flutter 页面,得到一个日志输出的端口号。
  • 然后 Android Studio 打开 Flutter 工程,在底部终端处输入 flutter attach –debug-port=日志输出端口号,然后附加成功即可。
  • 注意点:附加成功后,在终端处,按小写 r 只会刷新有有修改的文件,按大写的 R 会全部刷新,如果使用组件化的话,每次进入 Flutter 页面,Android Studio 附加成功后,都要先按大写 R 全部刷新一次 同步到最新代码。否则还会显示旧的页面。
3. 同时调试 iOS 和 Flutter(同时支持Flutter 和 Xcode断点的方式)

这种方法,需要先打开 Android Studio 选择 Attach Debugger to Android Process 等待 Flutter 页面连接,然后在 iOS 端,运行到 Flutter 页面,Android Studio 就会附加成功。

  • 首先打开 Flutter 工程,直接点击 Attach Debugger to Android Process,然后会等待 Flutter 页面连接。

  • frc 2357c32d6b38eccae2cd79b14d8ad1a4 - Flutter 开发 (2)优雅的 Flutter 组件化 混编方案
  • 然后运行 iOS 工程,进入 Flutter 页面。

  • 然后就会发现 Android Studio 已经显示在 同步文件了(Syncing files to device 张大森的 iPhone...

  • 同步完成即连接成功。

  • frc 087f462a80a8d55ea89cedb68026a768 - Flutter 开发 (2)优雅的 Flutter 组件化 混编方案

注意坑点

  • 我们可以看到 闪电符号 Flutter Hot Reload 和 返回绿色符号 Flutter Hot Restart
  • Flutter Hot Reload 为局部刷新,比如某个文件有改动,才会同步刷新此页面。Flutter Hot Restart可以理解为全部刷新,在 Android Studio 面板上也有对应按钮,相应也有对应快捷键。
  • 按照 Flutter 组件化的开发方式,我们首次附加连接成功后,一定要遵循一个步骤,先 点击 Flutter Hot Restart 进行全部刷新,再点击 Flutter Hot Reload 局部刷新。因为本人发现,如果最后一次刷新点击的是 Flutter Hot Restart 按钮,那么发现断点会不生效,只有点击 Flutter Hot Reload 后 触发的断点才会生效。
frc 681e2b2e4570d21387227ad89a2e6047 - Flutter 开发 (2)优雅的 Flutter 组件化 混编方案
frc 1d4283a8e5e2264d0879c55dcba7be92 - Flutter 开发 (2)优雅的 Flutter 组件化 混编方案

演示 Gif

  • frc 9fe2f6f6c8f84ecaa138e19038a75daa - Flutter 开发 (2)优雅的 Flutter 组件化 混编方案
    打断点.gif

总结

  • Android Studio 打开 Flutter 工程 ,点击 Attach Debugger to Android Process
  • 然后运行 Flutter 页面。
  • 然后点击Flutter Hot Restart (同步最新 Flutter 代码),在点击 Flutter Hot Reload 确保断点能够生效。

三、Flutter组件化 – 发布更新

frc 7862ee624f2590686466a1c288156959 - Flutter 开发 (2)优雅的 Flutter 组件化 混编方案
发布大概流程

(1)对 Flutter 工程 执行 flutter build ios 或 flutter build ios –debug 生成编译产物。
(2)把编译产物复制移动到 Flutter 私有库目录下。
(3)打包 上传更新私有库内容。
(4)主工程拉取最新版本。

版本更新说明
  • 开发期间:基本只用热更新进行开发代码。
  • 发布版本:一般可在上线前进行发布,所以组件版本更新,用的比较少。

四、一些坑点

(1)FlutterViewController 不释放
  • frc 34efb49b661c99f0af7ba97b0ce83b06 - Flutter 开发 (2)优雅的 Flutter 组件化 混编方案
  • 加载 Flutter 页面后,返回后,VC 不会释放。闲鱼有大神研究过这个问题,不过目前我们没有找到解决方案去释放 VC。
  • 我们使用单例持有了 VC,只能做到不每次进行叠加内存,不重新创建。每次进入 Flutter 页面都先重置一下。
  • 参考文章:https://www.jianshu.com/p/9ff7a9a5dfec
(1)不支持x86_64
  • 可以用xcode跑下,生成App.framewok,然后 lipo 命令合并下。
  • 我们目前不支持模拟器,这种方案没有进行测试。

其它

  • 如果有更好的调试方法,坑点解决方法,混编方法,欢迎交流反馈下。
下一篇文章 (主要涉及到 Flutter 开发的一些知识)
  • 教你 Flutter 如何与原生进行交互 待更新

参考文章

  • https://www.jianshu.com/p/5ffc83904971
  • https://www.jianshu.com/p/9ff7a9a5dfec
  • https://mp.weixin.qq.com/s/wdbVVzZJFseX2GmEbuAdfA

Flutter 开发 (2)优雅的 Flutter 组件化 混编方案