作者:闲鱼技术-字平
背景
闲鱼技术团队于2018年上半年率先引入了Flutter技术尝试实现客户端开发的统一,并成功改造和上线了复杂的商品详情业务。这一过程中,由于原有的iOS和安卓工程都已相当庞大,如何将Flutter无缝桥接到这些大工程并保证开发效率不受影响成为优先要解决的问题。
本文针对项目实践人员给出了一种通用的工程改造方案,希望为准备转型Flutter的团队提供参考。
问题
Flutter的工程结构比较特殊,由Flutter目录再分别包含Native工程的目录(即ios和android两个目录)组成。默认情况下,引入了Flutter的Native工程无法脱离父目录进行独立构建和运行,因为它会反向依赖于Flutter相关的库和资源。
image
典型的Flutter目录结构
很显然,在拥有了Native工程的情况下,开发者不太可能去创建一个全新的Flutter工程重写整个产品,因此Flutter工程将包含已有的Native工程,这样就带来了一系列问题:
1).构建打包问题:引入Flutter后,Native工程因对其有了依赖和耦合,从而无法独立编译构建。在Flutter环境下,工程的构建是从Flutter的构建命令开始,执行过程中包含了Native工程的构建,开发者要配置完整的Flutter运行环境才能走通整个流程;
2).混合编译带来的开发效率的降低:在转型Flutter的过程中必然有许多业务仍使用Native进行开发,工程结构的改动会使开发无法在纯Native环境下进行,而适配到Flutter工程结构对纯Native开发来说又会造成不必要的构建步骤,造成开发效率的降低。
目标
针对以上问题,我们提出了以下的改造目标,力求最小化Native工程对Flutter相关文件的依赖,使得:
1).Native工程可以独立地编译构建和调试执行,进而最大限度地减少对相关开发同学的干扰并使打包平台不再依赖Flutter环境及相关流程;
2).Native工程处在Flutter环境中时(即作为ios或android子目录)能够正确依赖相关库和文件,正常执行各类Flutter功能,如dart代码的构建,调试,hot reload等,保证Flutter环境下开发的正确性。
方案的制定
两种模式
首先定义Native工程处于独立目录环境下称为Standalone模式,处于Flutter目录下称为Flutter模式。目标中纯Native开发或平台打包就处于Standalone模式,Flutter对开发人员和打包平台来说是透明的存在,不会影响构建与调试;而Flutter的代码则在Flutter模式进行开发,其相关库的生成,编译和调试都走Flutter定义的流程。
屏幕快照 2018-07-17 上午10.52.30.png
两种模式
理清依赖
从上面的定义来看,改造的核心就是把Standalone模式提取出来,那么就要理清Standalone模式对Flutter的依赖,并将其提取成第三方的库,资源或源码文件。以iOS为例,通过阅读Flutter构建的源码,可知Xcode工程对Flutter有如下依赖:
1).App.framework:dart业务源码相关文件
2).Flutter.framework:Flutter引擎库文件
3).pubs插件目录及用于索引的文件:Flutter下的插件,包括各种系统的和自定义的channels
4).flutter_assets:Flutter依赖的静态资源,如字体,图片等
依赖引入的策略
改造过程中闲鱼尝试过两种依赖引入策略,以下分别进行阐述。
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模式下的构建流程和引用方式则不变。
优点:对Flutter自身的构建流程改动较少并且较彻底第解决了本地耦合的问题;
缺点:同步的流程变得更繁琐,Flutter内容的变动需要先同步到远程仓库再同步到Standalone模式方能生效。
PS. 闲鱼最终选择了这个策略。
远程依赖
改造的实现
目录的组织
Flutter模式下父工程目录下的ios和android的子目录分别包含对应的Native工程,代码管理上子工程可以使用git的submodule形式,保证目录间的独立。
远程依赖的实现
在Standalone模式下,Flutter的依赖内容都指向远程仓库中的对应文件,而在Flutter模式下依赖的方式不变。
1)向Standalone模式同步Flutter的变更
由于远程依赖的问题是同步变动比较麻烦,为此闲鱼开发了一系列脚本工具使该过程尽量自动完成。
假设Flutter的内容(可能是业务源码,引擎库或某些资源文件)发生变化,那么在Flutter模式下构建结束后,脚本会提取生成好的所有依赖文件拷贝到远程仓库,提交并打tag,然后依据打出的tag生成新的远程依赖说明(比如iOS下的podspec),最后在Standalone模式下修改Flutter的依赖至最新的版本,从而完成整个同步过程。
同步流程
2)同步的时机
建议在提测及灰度期间,每次Flutter业务的提交都能够触发同步脚本的执行和app打包;开发期间保持每日一次的同步即可。
总结
为解决引入Flutter后的工程适配问题,我们抽取了Flutter的相关依赖放到远程供纯Native工程进行引用,从而保证了Flutter与纯Native开发的相互独立与并行执行。
该方案已在闲鱼施行了几个版本,并反向输出给了Flutter团队,为其后续的hybrid工程组织计划提供了方向和参考。同时,相信该方案也可以为转型Flutter的团队提供帮助,当然项目间的差异也会导致方案的不同,因此如有更好的方法和意见也期望多多交流!
最后,闲鱼技术团队广招各类方向的达人,无论你是精通移动端,前端,后台,还是机器学习,音视频,自动化测试等,都欢迎投递简历加入我们,一同用技术改善生活!
简历投递:guicai.gxy@alibaba-inc.com
网友评论