混编的模式下,需要兼顾现有工程的开发、集成、架构等问题,例如,我们的代码里有Swift、有OC、有必须Bitcode的三方framework,这些会影响最终的集成方案。多人协作的项目中,还需要考虑开发人员对Flutter(任何其他语言也一样)的学习成本和迁移过程。
零、SDK版本选择
Flutter官方文档中提供了下载Flutter SDK的地址。但考虑到Flutter的升级频率相比某些跨平台方案来说要高一些,而且整体质量和团队投入度比较高,从而保证了可用性,因此,如果你希望以后方便升级Flutter SDK,那还是建议使用clone Github源码的方式。注意:官方建议使用Beta分支来做开发环境(master是日常开发环境,不保证稳定性)。一般而言,Beta环境最新代码默认会带某个版本号的Tag,如果没有,切换到最新的一个Tag上。
一、开发方式
相信大部分团队在做Flutter混编的项目时,一定是少部分人做Flutter开发,同时另一部分同学还是只做Native开发的,随着大家对Flutter的熟悉程度加深,再一步步转过来。在这过程中,Native开发同学需要对Flutter的开发环境、SDK及版本、Dart代码等无感,这就要求我们必须有“源码”和“产物”两套开发方式。
二、集成方案
工具选择方面,大部分基于Objective-C开发的iOS应用,都是用CocoaPods做组件管理工具的。如果是纯Swift,还可能是Cathage。
我们的项目是OC与Swift混编的,由于App体积并不大,当初选择用CocoaPods把依赖库以Framework形式集成,对App的启动速度影响并不明显。在Flutter混编中,选择基于CocoaPods(1.5.0+)的组件集成方案。
基于不同开发方式,我们需要提供快速切换的方式,对开发者来说,最好是不需要操作,简单的一步操作次之。由于是以组件形式依赖的,我们可以通过修改Podfile的变量,快速切换Pod命令,然后执行Pod install:
Flutter source code的两行代码是要读取并执行flutter工程(非SDK)的文件夹中的脚本,它是flutter自动生成的,我们做了一点修改。其主要是根据环境选择相应的SDK物料,将其放到Podfile中集成。后面我们会分析这个文件中的代码。
三、源码集成
在上述“集成方案”中的图片中可以看到,源码集成中用到了podhelp.rb。
这个ruby脚本中其实代码就这20行,主要就是把SDK文件夹中的Flutter.framework和Plugin相关的Pod集成进来。
四、Flutter产物集成
编译时用到了xcode_backend.sh,我们剖析下:
上图就是 Release.podspec 中做的唯一的事情——把他们打成一个包,集成进主工程。
这个图就是集成后的FlutterModule(前面说过,我们的工程中使用的是".framework",你的可以是".a"),其中:
App是我们自己写的Dart代码(名字就是App,别怀疑 :));
Flutter是SDK的产物;
FlutterPluginRegistrant是注册模块,如果你的Flutter项目中引用了Plugin,就会有这个产物。
hybrid_stack_manager是阿里闲鱼团队开发的 页面堆栈管理 模块,(在这里做个广告,康哥在我们开发过程中给了不少建议和帮助)。
tbridge是我们自己写的一个Plugin
Resources里面的flutter_assets是一个资源文件容器,Debug模式下如图。(Release模式下只有前三个和LICENSE,其他的几个都被打入了App中)
文件作用解释:(引用自https://www.jianshu.com/p/f44db0d088e5)
isolate_snapshot_data:用于加速 isolate 启动,业务无关代码,固定,仅和 flutter engine 版本有关;
vm_snapshot_data: 用于加速 Dart VM 启动的产物,业务无关代码,仅和 flutter engine 版本有关;
platform.dill:和 Dart VM 相关的 kernel 代码,仅和 Dart 版本以及 engine 编译版本有关。固定,业务无关代码;
kernel_blob.bin:业务代码产物
那么,这些产物怎么生成的呢?
答案是,从flutter工程中编译Run工程,或者用Flutter build命令生成,然后手动copy到主工程的。
首先,如果在yaml中加了plugin,就需要在命令行中执行flutter package get,flutter会在ios文件夹中自动生成podfile文件,执行了pod install后,打开Run.xcworkspace 在Run工程的Build Phases中,Run Script中已经加入了
/bin/sh "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build 这行命令,每次编译之前都会运行这个shell脚本,我们来看看它都做了些什么。
1. 根据编译环境(FLUTTER_BUILD_MODE)选择相应的SDK framework(在踩坑记录里有提到),拷贝到Flutter文件夹里
2. 根据debug/release来选择 执行flutter build aot 命令还是运行clang
3. 再次,执行 flutter build bundle,生成相应的文件。
4. 这时候,我们在本地的DerivedData文件中就可以找到相应的产物了,例如 yaml中的plugin等。
此时,再次编译整个Run工程,就可以以上述产物为依赖进行整个工程的编译链接了。