1.2.3 方案的制定
1.两种模式
首先将Native 工程处于独立目录环境下称为Standalone 模式,处于Flutter 目录下称为Flutter 模式。纯Native 开发或平台打包就处于Standalone模式,Flutter 对开发人员和打包平台来说是透明的,不会影响构建与调试。而Flutter 的代码则在Flutter 模式下进行开发,其相关库的生成、编译和调试都执行Flutter 定义的流程,如图1-13 所示。
图1-13 两种工程模式
2.厘清依赖
从模式的定义来看,既然改造的核心就是把Standalone 模式提取出来,那么就要厘清Standalone 模式对Flutter 的依赖,并将其提取成第三方的库、资源或源码文件。以iOS 为例,通过阅读Flutter 构建的源码,可知Xcode工程对Flutter 有如下依赖:
1)App.framework:Dart 业务源码相关文件。
2)Flutter.framework:Flutter 引擎库文件。
3)pubs 插件目录及用于索引的文件:Flutter 下的插件,包括各种系统的插件和自定义的channels(桥接通道)
4)flutter_assets:Flutter 依赖的静态资源,如字体和图片等。
3.依赖引入的策略
在改造过程中,闲鱼尝试过两种依赖引入策略,下面分别进行阐述。
(1)本地依赖。通过修改Flutter 构建流程,将其库文件、源码和资源直接放置到Native 工程的子目录中进行引用,以iOS 为例,就是将Flutter.framework 及相关插件等做成本地的Pod 依赖,也将资源复制到本地进行维护。由此,Standalone 模式便具备了独立构建和执行的能力,对于纯Native 开发人员来说,Flutter 只是一些二方库与资源的合集,无须关注。而在Flutter 模式下,Dart 源码的构建流程不变,不影响编译和调试。同时,由于是本地依赖,在Flutter 模式下的各种改动也可以实时地同步到Native 工程的子目录中。提交修改后,Standalone 模式也就拥有了最新的Flutter 相关功能。
优点:将Flutter 相关内容的改动同步到Standalone 模式也比较方便;
缺点:需要对Flutter 原有的构造流程进行稍复杂的改动,并且与后续的Flutter 代码合并会有冲突,且Native 工程与Flutter 的代码、库及资源等内容还是耦合在本地,不够独立。
(2)远程依赖。远程依赖的想法是将Flutter 所有依赖内容都放在独立的远端仓库中,在Standalone 模式下引用远程仓库中的相关资源、源码和库文件,在Flutter 模式下的构建流程和引用方式不变,如图1-14 所示。
优点:对Flutter 自身的构建流程改动较少,较彻底地解决了本地耦合的问题。
缺点:同步的流程变得更烦琐,Flutter 内容的变动需要先同步到远程仓库后再同步到Standalone 模式方能生效。
图1-14
1.2.4 改造的实现过程
1.目录的组织
在Flutter 模式下,父工程目录下的iOS 和Android 的子目录分别包含对应的Native 工程。在代码管理上,子工程可以使用Git 的Submodule 形式,保证目录间的独立。
2.远程依赖的实现
在Standalone 模式下,Flutter 的依赖内容都指向远程仓库中的对应文件,而在Flutter 模式下依赖的方式不变。
(1)向Standalone 模式同步Flutter 的变更。由于远程依赖的问题是同步变动比较麻烦,为此闲鱼开发了一系列脚本工具,使该过程尽量自动完成。假设Flutter 的内容(可能是业务源码、引擎库或某些资源文件)发生变化,那么在Flutter 模式下构建结束后,脚本会提取生成好的所有依赖文件并将其复制到远程仓库,提交并打标签,然后依据打出的标签生成新的远程依赖说明(如iOS 下的podspec 文件),最后在Standalone 模式下将Flutter 的依赖修改至最新的版本,从而完成整个同步过程,如图1-15 所示。
图1-15
(2)同步的时机
建议在提测及灰度期间,每次Flutter 业务的提交都能够触发同步脚本的执行和App 打包;在开发期间,保持每日一次的同步即可。
为解决引入Flutter 后的工程适配问题,闲鱼抽取了Flutter 的相关依赖放到远程供纯Native 工程进行引用,从而保证了Flutter 与纯Native 开发的相互独立与并行执行。
该方案已在闲鱼施行了几个版本,并反向输出给了Flutter 团队,为其后续的hybrid 工程组织计划提供了方向和参考。同时,相信该方案也可以为转型Flutter 的团队提供帮助,虽然项目间的差异也会导致方案的不同,但是实施的思路依然有借鉴价值。