Flutter-混合工程的持续集成实践

1,050 阅读4分钟

在拥有了 Native 工程的情况下,开发者不太可能去创建一个全新的 Flutter 工程重写整个产品,因此Flutter工程将包含已有的 Native 工程,这样就带来了一系列问题:

  • 构建打包问题: 引入Flutter后,Native 工程因对其有了依赖和耦合,从而无法独立编译构建。在 Flutter 环境下,工程的构建是从 Flutter 的构建命令开始,执行过程中包含了 Native 工程的构建,开发者要配置完整的 Flutter 运行环境才能走通整个流程;
  • 混合编译带来的开发效率的降低: 在转型 Flutter 的过程中必然有许多业务仍使用 Native 进行开发,工程结构的改动会使开发无法在纯 Native 环境下进行,而适配到 Flutter 工程结构对纯Native 开发来说又会造成不必要的构建步骤,造成开发效率的降低。

期望

希望能够做到当项目混编的时候,没有开发 flutter 的同学能够完全脱离 flutter,不需要flutter 项目代码和安装 flutter 环境;而写 flutter 的团队成员能够按照原有的混编方式以方便开发和调试。

我们发现不管是 iOS 还是 Android 工程,依赖与 Flutter 的文件有:

  • Flutter库和引擎:Flutter.framework
  • Flutter工程产物:App.framework
  • Flutter Plugin:编译出来的各种plugin的framework

那我们只需要将这三部分的编译结果抽取出来,打包成一个 SDK 依赖的形式提供给 Native 工程,就可以解除 Native 工程对 Flutter 工程的直接依赖。

常见的引入方式

1.直接引入

原生项目通过cocoapods直接引入,需要依赖 Flutter 环境,Podfile里面的引入代码如下:

flutter_application_path = '../flutter_module'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')

platform :ios, '9.0'
target 'NativeDemo' do
  install_all_flutter_pods(flutter_application_path)
  use_frameworks!
end

执行 pod install

截屏2021-12-12 下午3.33.26.png

打开原生工程进行使用

截屏2021-12-12 下午3.49.10.png

#import <Flutter/Flutter.h>

FlutterViewController *flutterVC = [[FlutterViewController alloc] init];
[self presentViewController:flutterVC animated:YES completion:nil];

2. Framework方式引入

通过命令把 flutter 工程编译成 framework,然后引入原生工程中

flutter build ios-framework --output=../flutter_app

截屏2021-12-12 下午3.51.14.png 编译完成后,发现编译出了 Debug、Profile、Release 三种环境的framework

  • Debug 具备调试功能
  • Profile 既具备debug的调试功能,又具备release的性能
  • Release 发布环境

每个环境包含 App.xcframeworkFlutter.xcframework,App.xcframework是我们的Flutter程序代码,Flutter.xcframework 包含Flutter的运行环境和引擎

截屏2021-12-12 下午3.52.55.png

这个时候让电脑上没有Flutter环境,看是否能正常跑Flutter代码?

截屏2021-12-12 下午3.55.18.png

在工程中添加framework

截屏2021-12-12 下午3.55.59.png

然后运行原生工程,发现是可以运行成功并且使用Flutter的。

3. Cocoapods 方式引入

进入flutter_module目录,然后编译

flutter build ios-framework --cocoapods --output=../flutter_app

发现也是编译出了三种环境,和编译Framework的区别是,flutter引擎是 Flutter.podspec

截屏2021-12-12 下午4.04.30.png

截屏2021-12-12 下午4.08.26.png

然后在原生工程通过 pod 引入:

pod 'Flutter',: podspec => 'flutter_app/Profile/Flutter.podspec'

原生工程添加 App.xcframework

截屏2021-12-12 下午4.05.53.png

同样运行原生工程是没有问题的。

优化 Cocoapods 引入,实现工程自动化

一方面通过自动化减少人工成本,也减少人为失误。另一方面是做好版本控制,自动化的形式来做版本控制。

创建工程仓库

这个里面包含 原生Flutter工程 代码

截屏2021-12-12 下午4.29.14.png

执行命令创建仓库:

截屏2021-12-12 下午4.37.10.png

这里会提示输入 github 账号和密码,现在都是采用 Token 配置密码了。进入 Settings->Developer settings, 然后创建个人令牌

截屏2021-12-12 下午4.31.14.png

创建成功后复制 token,在终端粘贴回车

截屏2021-12-12 下午4.31.33.png

这时就可以看到成功创建了仓库了

截屏2021-12-12 下午4.38.40.png

添加原生工程到仓库

依次执行命令:

git add.
git commit -m ""
git push

成功后可以看到仓库里面就包含工程代码了

截屏2021-12-12 下午5.14.01.png

添加自动化脚本

截屏2021-12-12 下午4.43.49.png

截屏2021-12-12 下午4.47.13.png

脚本代码

这里是每次 push 代码脚本都会执行

name: FlutterCI #CI名称
on: [push] #触发条件push操作!

jobs:
  check:
    name: Test on ${{ matrix.os }}
    #运行在哪个平台这里是MacOS平台
    runs-on: macos-latest
    
    steps:
      - uses: actions/checkout@v1
      #三方flutter的Action,它可以在服务器配置一个Flutter环境
      - uses: subosito/flutter-action@v1
        with:
          flutter-version: '2.5.3'
          channel: 'stable'
      #想让我们CI做的事情!
      - run: cd flutter_module && flutter build ios-framework --cocoapods --output=../NativeDemo/Flutter 
      - run: |
         git add .
         git commit -m 'update flutter framework'
     
      - name: Push changes
        uses: ad-m/github-push-action@master
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}

添加完脚本后可以看到正在编译Flutter工程了

截屏2021-12-12 下午4.47.58.png

编译完成了

截屏2021-12-12 下午4.52.22.png

这个时候可以看到原生项目里面多了一个Flutter文件夹,里面包含 Debug、Profile、Release三个环境的framework。

截屏2021-12-12 下午5.21.25.png

拉取远程代码到本地

git pull

配置 cocoapods

到项目里面配置cocoapods,和上面通过cocoapods引入项目是一样的操作,在Podfile文件里面引入

platform :ios, '9.0'
target 'NativeDemo' do
  use_frameworks!
  pod 'Flutter',:podspec => 'Flutter/Debug/Flutter.podspec'
end

截屏2021-12-12 下午5.27.14.png

然后运行工程是没有问题的,说明配置成功了。然后依次执行

git add.
git commit -m ""
git push

push 成功后可以看到脚本又开始自动构建了

截屏2021-12-12 下午5.31.32.png

注意:这里只是一个简单的工程,假如大的原生项目嵌入Flutter我们要考虑的因素有很多,这里只是一个思路。每次发布上线的时候,记得选择 Release 里面的 framework。