简单介绍
开始学习flutter之后比较关注的实际问题之一就是怎么混合开发,今天主要思考一下Android项目中native(android library)+flutter以什么形式进行混合开发以及怎么生成aar的问题。以下的总结是我的拙见,在这里也是想确认大家是否也遇到了相同的问题,有不对的欢迎批评指教~~
大致的混合开发形式以及遇到的问题为:
1、使用官方的flutter module形式,依据官方给出的集成方案。
该方案中当native项目为android library时打包aar可能会存在一些问题。由于android library打包成的aar并不会包含其依赖的aar或module的内容,所以此时native项目生成的aar并不会包含flutter module中的内容,flutter module需要单独再打一个aar出来给调用方。
2、方案1存在的问题为aar文件本身不包含依赖传递的信息,很简单想到借助于maven pom传递依赖,即形成第2种方案;flutter module项目单独生成aar,上传至maven远程或本地仓库,在android library项目中通过仓库获取该aar,同样android library生成的aar也会上传到仓库,调用方通过仓库获取,所有依赖关系都会跟随pom文件进行传递。
该方案可能存在的问题为: 1)官方推荐的flutter module的目录结构为.android、.ios、lib,而且其中.android和.ios目录在.gitignore中,即不推荐上传module中的native项目(其他人从git clone该项目之后,通过执行flutter packages get即可自动生成.android、.ios目录),则不推荐在.android中添加业务代码或者自定义操作,那么就不方便执行flutter module的aar自动生成以及上传; 2)android library通过aar依赖flutter module不便于开发时的测试。
3、第三种方案为不使用官方推荐的flutter module形式,将dart代码,native项目组合为一个工程,类似于Flutter Application结构,只是native项目不是com.android.application,而是com.android.library。
该方案不存在flutter module怎么被依赖的问题,只需要在android library项目中生成aar即可。
选择方案
个人理解看下来可能第3种方案比较方便,但是如果想使用官方flutter module形式且开发方便,倾向于选择第一种方案:即通过官方集成方案将android library依赖于flutter module。
所以需要试图解决方案1存在的问题,实现android library项目生成的aar能包含其依赖的flutter module的内容。
解决问题
一开始解决问题的目标定为合并多个aar,github上也有生成fat-aar的解决方案。但是由于目前flutter module只包含自动生成的android原生代码,没有资源文件(如下第一张图),并且flutter module生成的aar(结构如下第二张图)中比较重要的几部分为asssets、libs、jni、classes.jar,所以可以预想将问题简化为:android library项目生成的aar只需要包含上面这几部分或许就ok了,先忽略manifest、res等的合并。
确定了解决方案之后,就开始分以下几点开始试验:
1、集成flutter module的assets
从flutter.gradle文件中可得知,assets目录中内容的生成主要依靠task依赖执行:
Task mergeAssets = project.tasks.findByPath(":${mainModuleName}:merge${variant.name.capitalize()}Assets")if (mergeAssets){ mergeAssets.dependsOn(copyFlutterAssetsTask)}复制代码
其中copyFlutterAssetsTask还会依赖于其他flutter task,执行flutter build操作;所需要的assets文件最后都会被放入build/intermediates/library_assets/${buildType}/package${buildTypeCapitalize()}Assets/out目录中,其中buildType即为debug或者release或其他。
所以为了合并assets需要完成如下两件事情:
1)在android library中执行assemble操作时需要能执行flutter module的copyFlutterAssetsTask任务; 解决方案为在android library的build.gradle中添加如下内容:
def flutterProject = project.rootProject.findProject(":flutter") flutterProject.afterEvaluate { flutterProject.android.libraryVariants.all { variant -> if (variant.name == 'debug' || variant.name == 'release') { project.tasks.findByName("package${variant.name.capitalize()}Assets").dependsOn flutterProject.tasks.findByName("copyFlutterAssets${variant.name.capitalize()}") } } }复制代码
同时要在android library的root project的build.gradle中添加如下配置,其中'mylibrary'为android library的module name;目前不是很清楚不增加为什么无法执行上面配置的flutterProject.afterEvaluate,所以这一点还需要优化:
ext { mainModuleName = 'mylibrary' }复制代码
2)需要能将上面flutter module的assets out目录添加为android library的assets目录;
if (variant.name == 'release') {android.sourceSets.release.assets.srcDirs += flutterProject.getBuildDir().getAbsolutePath() + "/intermediates/library_assets/${variant.name}/package${variant.name.capitalize()}Assets/out"} else if (variant.name == 'debug') {android.sourceSets.debug.assets.srcDirs += flutterProject.getBuildDir().getAbsolutePath() + "/intermediates/library_assets/${variant.name}/package${variant.name.capitalize()}Assets/out"}复制代码
2、集成flutter module的libs和jni
flutter.gradle中的task会将flutter sdk中的flutter.jar以及相关的so文件自动添加为flutter module的依赖,具体内容如下:
private void addFlutterJarApiDependency(Project project, buildType, Task flutterX86JarTask) { project.dependencies { String configuration; if (project.getConfigurations().findByName("api")) { configuration = buildType.name + "Api"; } else { configuration = buildType.name + "Compile"; } add(configuration, project.files { String buildMode = buildModeFor(buildType) if (buildMode == "debug") { [flutterX86JarTask, debugFlutterJar] } else if (buildMode == "profile") { profileFlutterJar } else if (buildMode == "dynamicProfile") { dynamicProfileFlutterJar } else if (buildMode == "dynamicRelease") { dynamicReleaseFlutterJar } else { releaseFlutterJar } }) } }复制代码
这一点的解决方案即为将这些依赖添加为android library的依赖,具体做法为在android library的build.gradle中添加如下内容:
flutterProject.configurations.each { conf -> conf.getDependencies().each { dep -> if (dep instanceof org.gradle.api.artifacts.FileCollectionDependency) { org.gradle.api.file.FileCollection fileCollection = dep.getFiles().filter { file -> file.getName().contains("flutter") } if (!fileCollection.isEmpty()) { if (project.configurations.contains(conf)) { project.dependencies { add(conf.getName(), dep) } } } } } }复制代码
3、集成classes.jar里面的内容
这一点也比较简单,因为flutter module里面java代码作为android library的源码,具体做法为
android.sourceSets.main.java.srcDirs += flutterProject.getProjectDir().getAbsolutePath() + "/src/main/java"复制代码
经过以上几点的集成,android library中assembleRelease或者assembleDebug生成的aar就会包含flutter module中几大重要的内容,然后将此aar放在另一个android application中引用,也可以正常调起flutter module中的flutter页面,说明集成成功。当然如果以后flutter module在资源、manifest清单文件中有更多重要的内容,则需要考虑这些内容的合并,就会比较复杂。
目前的实现方案比较简单,希望大家一起讨论一下是否可选第1种方案进行混合开发、其存在的问题是否需要解决以及大家一般怎么解决(我不希望自己又走到了某种歪门邪道。。。)~~;当然如果本文的方案值得使用,后续还会继续进行优化。