背景 目前准备试水Flutter,但是多数native开发是不了解Flutter,因此需要设计一种比较舒服的集成方式。混编方案方案考量如果直接采用Flutter工程结构来作为日常开发,那这部分Native开发也需要配置Flutter环境,相当程度的了解Flutter一些技术,成本比较大。同时如果工程耦合,对于开发过程也是很难受的 基于以上两点思考,针对AndroidiOS有如下方案:Android 先看下官方的集成方式:setting。gradlesetBinding(newBinding(〔gradle:this〕))evaluate(newFile(。。managementcenter。androidincludeflutter。groovy))123456build。gradledependencies{implementationproject(:flutter)。。。。。。。。。。。}12345 这种方式使得工程强耦合,虽然便于开发调试,但是违背了第一点,大多数native同学都需要配置Flutter环境,成本很大。iOS官方iOS混编方案简介 传送门在native项目Podfile中通过evalbinding特性注入podhelper。rb脚本,在podinstallupdate时执行此脚本,脚本主要处理:Pod本地依赖Flutter引擎(Flutter。framework)与Flutter插件注册表(FlutterPluginRegistrant)Flutter插件通过flutterpackagesget指令安装后生成的。flutterplugins文件解析,然后Pod本地依赖所有的插件在podinstall执行完的钩子postinstall中,获取当前podtarget工程对象,导入Generated。xcconfig配置,其中都为环境变量的配置,主要为后续的xcodebackend。sh脚本执行做准备在构建阶段BuildPhases中注入构建是需要执行的xcodebackend。sh脚本,脚本主要完成Flutter产物的构建并将其添加到对应的native工程中去,后续会进一步介绍此脚本优点无缝开发,配置好后就可以只在Flutter工程内进行业务开发,无缝同步到native工程中不需要单独拆分组件,免去管理组件的版本及发布成本缺点非常耦合,需要修改原有native工程配置,需要添加特定脚本去编译Flutter需要修改原有pod的xcconfig配置所有团队开发成员都必须要配置Flutter开发环境才能编译成功小结 基于以上思考,同时考虑到某个Flutter业务模块可能会引入到不同的App中,同时考虑到某个业务实现方式方面的解耦(某个业务可能用native,flutter,weex开发),有以下方案(中间产物库每个Flutter业务模块都是独立的): Android: iOS: Flutter产物结构Android iOS 关于编译模式了解更多可参考查看Flutter的编译模式。Flutter产物收集Android 在Android端集成Flutter较为简单,只需要获取到上文所讲的Flutter产物即aar文件。但是由于插件文件散落每次获取比较麻烦所以目前简单用脚本收集。 脚本收集主要是依靠项目里。flutterplugins文件,该文件会记录flutter项目中引用的插件名以及本地路径等,因此可以通过该路径抓取插件的aar文件。fromshutilimportcopyfileimportosimportrequests抓取文件类型BuildReleaseTrueaarTyperelease。aarifBuildReleaseelsedebug。aarpluginFilePath。。。flutterplugins当前项目的flutter。aarcurrentFlutterPath。。。androidFlutterbuildoutputsaar输出地址outputFilePathos。path。abspath(flutteraar。py)。replace(flutteraar。py,aars)endPathandroidbuildoutputsaardefcollectaar(plugins):allcollectionsuccessTrueifos。path。exists(outputFilePath):print(copyaarto:outputFilePath)else:print(targetpath:outputFilePathnotexist)os。makedirs(outputFilePath)print(createtargetpath:outputFilePath)forkey,valueinplugins。items():aarpathvaluekeyaarTypetry:copyfile(aarpath,outputFilePathkeyaarType)print(copyflutteraarsuccessatpath:aarpath)exceptIOError:allcollectionsuccessFalseprint(copyflutteraarerroratpath:aarpath)passfileobjectopen(pluginFilePath,r)try:pluginmap{}forlineinfileobject:arrayline。split()pluginmap〔array〔0〕〕array〔1〕。replace(,)endPathpluginmap〔flutter〕currentFlutterPathcollectaar(pluginmap)finally:fileobject。close()123456789101112131415161718192021222324252627282930313233343536373839404142434445464748 目前该python脚本只抓取Release的aar文件,如果需要获取debug的可以手动修改BuildReleaseFalse1 执行抓取脚本。flutteraar。sh!usrbinenvbashcd。。cd。androidechostartclean。gradlewcleanechostartassembleRelease。gradlewassembleReleasecd。。cdandroidbuildechocleanoldaarfilermrfaarsechostartcopyaarfile只抓取releasepythonflutteraar。pyechocopyaarfilefinish12345678910111213141516171819 脚本执行完Flutter产物aar文件统一生成在根目录下androidbuild文件夹中。 iOS 通过查看Flutter编译脚本xcodebackend。sh和测试单独引入编译产物,发现其实只要拥有Flutter的编译产物,宿主项目就可以接入Flutter的功能。脚本简单分析engineFlutter。frameworkFlutter核心库拷贝Flutter。frameworkif〔〔e{projectpath}。ios〕〕;thenRunCommandrmrf{deriveddir}enginemkdir{deriveddir}engineRunCommandcpr{flutterpodspec}{deriveddir}engineRunCommandcpr{flutterframework}{deriveddir}engineRunCommandfind{deriveddir}engineFlutter。frameworktypefexecchmodaw{};elseRunCommandrmrf{deriveddir}Flutter。frameworkRunCommandcpr{flutterframework}{deriveddir}RunCommandfind{deriveddir}Flutter。frameworktypefexecchmodaw{};fi1234567891011debug模式下Dart业务代码编译(JIT)App。frameworkRunCommandeval(echostaticconstintMoo88;xcrunclangxc{archflags}dynamiclibXlinkerrpathXlinkerexecutablepathFrameworksXlinkerrpathXlinkerloaderpathFrameworksinstallnamerpathApp。frameworkAppo{deriveddir}App。frameworkApp)1234567非debug模式下Dart业务代码编译(AOT)App。frameworkRunCommand{FLUTTERROOT}binfluttersuppressanalytics{verboseflag}buildaotoutputdir{builddir}aottargetplatformiostarget{targetpath}{buildmode}iosarch{archs}{localengineflag}{trackwidgetcreationflag}12345678910资源文件等打包flutterassetsStreamOutputAssemblingFlutterresources。。。RunCommand{FLUTTERROOT}binfluttersuppressanalytics{verboseflag}buildbundletargetplatformiostarget{targetpath}{buildmode}depfile{builddir}snapshotblob。bin。dassetdir{deriveddir}App。frameworkflutterassets{precompilationflag}{localengineflag}{trackwidgetcreationflag}123456789101112方案分析设计 插件统一编译成。a库,添加对应头文件App。framework及engineFlutter。framework添加目前初期demo将上述生成的产物统一放入到私有库当中,然后native宿主工程pod依赖此库,只需要在使用Flutter代码的地方import对应的头文件即可正常使用脚本编写echo清理flutter历史编译fluttercleanecho重新生成plugin索引flutterpackagesgetecho生成App。framework和flutterassetsflutterbuildiosdebugecho获取所有plugin并找到头文件whilereadrlinedoif〔〔!line〕〕;thenarray({line})pluginname{array〔0〕}cd。iosPodsecho生成lib{pluginname}。a。。。usrbinenvxcrunxcodebuildbuildconfigurationReleaseARCHSarm64armv7target{pluginname}BUILDDIR。。。。buildiossdkiphoneosquietusrbinenvxcrunxcodebuildbuildconfigurationDebugARCHSx8664target{pluginname}BUILDDIR。。。。buildiossdkiphonesimulatorquietecho合并lib{pluginname}。a。。。lipocreate。。。。buildiosDebugiphonesimulator{pluginname}lib{pluginname}。a。。。。buildiosReleaseiphoneos{pluginname}lib{pluginname}。ao。。。。productlib{pluginname}。aecho复制头文件classes{array〔1〕}iosClassesforheaderinfindclassesname。h;docpfheader。。。。productdoneelseecho读取文件出错fidone。flutterpluginsecho生成注册入口的二进制库文件forregenternameinFlutterPluginRegistrantdoecho生成libFlutterPluginRegistrant。a。。。usrbinenvxcrunxcodebuildbuildconfigurationReleaseARCHSarm64armv7targetFlutterPluginRegistrantBUILDDIR。。。。buildiossdkiphoneosusrbinenvxcrunxcodebuildbuildconfigurationDebugARCHSx8664targetFlutterPluginRegistrantBUILDDIR。。。。buildiossdkiphonesimulatorecho合并libFlutterPluginRegistrant。a。。。lipocreate。。。。buildiosDebugiphonesimulatorFlutterPluginRegistrantlibFlutterPluginRegistrant。a。。。。buildiosReleaseiphoneosFlutterPluginRegistrantlibFlutterPluginRegistrant。ao。。。。productlibFlutterPluginRegistrant。aecho复制头文件classes。。FlutterFlutterPluginRegistrantClassesforheaderinfindclassesname。h;docpfheader。。。。productdonedone123456789101112131415161718192021222324252627282930313233343536373839404142434445后续规划脚本优化,添加自动pod库检测及上传App。frameworkFlutter。framework体积太大,放到git仓库不太友好,考虑后续上传到CDN,然后在pod安装的时候预先执行脚本把两个产物拉下来Flutter产物上传Android 上面产物搜集完成后,需要上传maven仓库,方便集成以及版本控制applyplugin:youzan。maven。uploadzanMavenUpload{version0。0。2childGroupflutterapub}uploadItems{fluttertoast{targetFilefile(。。。。androidbuildaarsfluttertoastrelease。aar)}imagepicker{targetFilefile(。。。。androidbuildaarsimagepickerrelease。aar)}。。。。。。。。。。。。}123456789101112131415161718 因此引用链如下:Android iOS 总结 以上比较全面的描述了有赞的Flutter混编方案,目前有赞已经在内部使用的App上使用Flutter开发了一些页面作为试点。后续会考虑在线上App试点,目前正在进行Flutter基础库的搭建,之后会专门有文章分享。