最近对之前项目里面依赖的Flutter模块进行了一次升级。因为从1.x升级的时候3.0的flutter需要安卓原生适配compilesdkversion,所以我们APP使用的Flutter版本为2.8.1,现在app的构建版本升上来了,于是对之前的Flutter进行了升级。
Flutter开发的页面从2.8升级到3.3.8倒没有遇到什么问题。但是最后把Flutter模块打包成 aar 以及依赖到安卓工程里面的时候遇到了问题。
flutter升级到 v3.3.8 之后,使用 flutter build aar--no-profile--no-debug
打出来的aar结构如下图:
├── flutter
│ ├── 1.34.1.6-bae-SNAPSHOT
│ │ ├── flutter-1.34.1.6-bae-20221125.160600-1-debug.aar
│ │ ├── flutter-1.34.1.6-bae-20221125.160600-1-profile.aar
│ │ ├── flutter-1.34.1.6-bae-20221125.160600-1-release.aar
│ │ ├── maven-metadata.xml
│ │ ├── flutter-1.34.1.6-bae-20221125.160600-1.module
│ ├── maven-metadata.xml
│ ├── maven-metadata.xml.md5
│ ├── maven-metadata.xml.sha1
├── flutter_debug
│ ├── 1.34.1.6-bae-SNAPSHOT
├── flutter_profile
│ ├── 1.34.1.6-bae-SNAPSHOT
└── flutter_release
├── 1.34.1.6-bae-SNAPSHOT
并且命令行里面会提示:
dependencies {
releaseImplementation 'com.netease.bae.flutter.baeflutter:flutter:1.34.1.6-bae-SNAPSHOT:release'
}
这里和 2.x 相比,打包产物其实有了变化。之前用 2.x 打包的时候,生成的 flutter module 产物只有 flutter_release
文件夹下面的内容。
并且命令行里面的提示也只是:
releaseImplementation 'com.netease.bae.flutter.baeflutter:flutter_release:1.34.1.6-bae-SNAPSHOT'
这里我们把依赖替换成 3.3.8 提示的内容,暂时忽略这里的 :release
当我们依赖好aar后,编译正式版本的时候会出现编译错误:
Could not determine the dependencies of task ':app:compileAReleaseTestRenderscript'.
> Could not resolve all task dependencies for configuration ':app:AReleaseTestCompileClasspath'.
> Could not resolve com.netease.bae.flutter.baeflutter:flutter:xxxx.
Required by:
project :app > project :appservice
> No matching variant of com.netease.bae.flutter.baeflutter:flutter:xxxx:20221122.043716-1 was found. The consumer was configured to find an API of a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'releaseTest', attribute 'product' with value 'A', attribute 'org.jetbrains.kotlin.platform.type' with value 'androidJvm' but:
- Variant 'debugVariantAllApiPublication' capability com.netease.bae.flutter.baeflutter:flutter:xxxx declares an API of a component:
- Incompatible because this component declares a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'debug' and the consumer needed a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'releaseTest'
- Other compatible attributes:
- Doesn't say anything about org.jetbrains.kotlin.platform.type (required 'androidJvm')
- Doesn't say anything about product (required 'A')
- Variant 'debugVariantAllRuntimePublication' capability com.netease.bae.flutter.baeflutter:flutter:xxxx declares a runtime of a component:
- Incompatible because this component declares a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'debug' and the consumer needed a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'releaseTest'
- Other compatible attributes:
- Doesn't say anything about org.jetbrains.kotlin.platform.type (required 'androidJvm')
- Doesn't say anything about product (required 'A')
- Variant 'profileVariantAllApiPublication' capability com.netease.bae.flutter.baeflutter:flutter:xxxx declares an API of a component:
- Incompatible because this component declares a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'profile' and the consumer needed a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'releaseTest'
- Other compatible attributes:
- Doesn't say anything about org.jetbrains.kotlin.platform.type (required 'androidJvm')
- Doesn't say anything about product (required 'A')
- Variant 'profileVariantAllRuntimePublication' capability com.netease.bae.flutter.baeflutter:flutter:xxxx declares a runtime of a component:
- Incompatible because this component declares a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'profile' and the consumer needed a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'releaseTest'
- Other compatible attributes:
- Doesn't say anything about org.jetbrains.kotlin.platform.type (required 'androidJvm')
- Doesn't say anything about product (required 'A')
- Variant 'releaseVariantAllApiPublication' capability com.netease.bae.flutter.baeflutter:flutter:xxxx declares an API of a component:
- Incompatible because this component declares a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'release' and the consumer needed a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'releaseTest'
- Other compatible attributes:
- Doesn't say anything about org.jetbrains.kotlin.platform.type (required 'androidJvm')
- Doesn't say anything about product (required 'A')
- Variant 'releaseVariantAllRuntimePublication' capability com.netease.bae.flutter.baeflutter:flutter:xxxx declares a runtime of a component:
- Incompatible because this component declares a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'release' and the consumer needed a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'releaseTest'
- Other compatible attributes:
- Doesn't say anything about org.jetbrains.kotlin.platform.type (required 'androidJvm')
- Doesn't say anything about product (required 'A')
> Could not resolve com.netease.cloudmusic.look.flutterhybrid:flutterhybrid:xxxx.
这里看起来是 variant
发生了冲突。第一反应对这个报错是比较奇怪的,因为不理解为什么一个aar被依赖的时候,还会存在 variant
但是经过对比,2.8.1 和 3.3.8 打包产物比起来,3.3.8 多出了一个 .module
文件,这个文件的内容是 json 格式的,其中包括了variants 的定义,
为了方便阅读,我只留了name和一些关键的key,内容如下:
"variants": [
{
"name":"debugVariantAllApiPublication",
"attributes":{},
"dependencies":[
{
"group":"io.flutter",
"module":"flutter_embedding_debug",
"version":{
"requires":"1.0.0-857bd6b74c5eb56151bfafe91e7fa6a82b6fee25"
}
}
],
"files": [
{
"name":"",
"url":"",
"size":36580,
"sha512":"",
"sha256":"",
"sha1":"",
"md5":""
}
]
},
{
"name":"releaseVariantAllApiPublication",
"attributes":{},
"dependencies":[],
"files": []
}
//....
]
这个文件里面定义了 debugVariantAllApiPublication
、 profileVariantAllApiPublication
和 releaseVariantAllApiPublication
三个 variant,
里面分别定义了自己依赖的其他 aar 以及当前文件的名称、url、大小和签名。
到这里我们大概能明白依赖的时候 :release
的含义了,它会帮我们选择需要的 aar 文件。而上面的编译错误,就是因为我们的 APP 里面定义了 buildType 为 releaseTest
,所以导致编译失败。
警告不断努力的 Google 和 文档翻阅,找到了 2 个解决方案。
matchingFallbacks
在 gradle 的配置里,我们可以通过 matchingFallbacks
来处理应用包含依赖项不包含的 build 类型。
在我们应用的 build.gradle
里面都需要在 releaseTest
下面添加:
releaseTest {
matchingFallbacks = ['debug', 'release']
}
经过实践,我们需要把所有的业务 module 的 build.gradle
都添加上这个配置,否则就会不生效,这个对一个业务 module 比较多的 APP 来说还是比较麻烦的。
ComponentMetadataRule
通过阅读 gradle 文档,可以发现一种解决方案:https://docs.gradle.org/current/userguide/component_metadata_rules.html
我们可以定义我们自己的 ComponentMetadataRule
修改元数据。给对应的依赖项添加我们自己的 variant。
@CacheableRule
class FlutterRule implements ComponentMetadataRule {
@Override
void execute(ComponentMetadataContext componentMetadataContext) {
componentMetadataContext.details.addVariant("releaseTestVariantAllApiPublication", "debugVariantAllRuntimePublication") {
attributes {
def ret = it.keySet().find {
it.name == "com.android.build.api.attributes.BuildTypeAttr"
}
if (ret != null) {
attribute(ret, "releaseTest")
}
}
}
}
}
代码如上图所示,通过 ComponentMetadataContext#addVariant
,我们可以以现有的 variant 为基础,定义并添加一个新的 variant。
这里我们根据 debugVariantAllRuntimePublication 创建了 releaseTestVariantAllApiPublication,并且把 BuildTypeAttr 属性设置为当前的 buildType。
接下来就是让这个规则全局对 Flutter 相关的依赖生效,我们可以使用 dependencyResolutionManagement
在 setting.gradle
里面定义。
dependencyResolutionManagement {
rulesMode.set(RulesMode.PREFER_SETTINGS)
components {
withModule("com.netease.bae.flutter.baeflutter:flutter", FlutterRule)
}
}
withModule 里面添加依赖项的 group:module
即可。因为项目内的 gradle 设置默认情况下覆盖这里的设置,使用需要添加 RulesMode.PREFER_SETTINGS
,否则不会生效。
需要注意的是,网上很多文档说 dependencyResolutionManagement
是 AGP7 开始的 api, 其实这里是有点误解的。Gralde api 文档里面这个方法标记的是 since 6.8,所以即使你使用的 AGP 版本是4.x,也只是需要修改 gradle-wrapper.properties
里面的 gradle 版本即可:
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-all.zip
通过自定义 ComponentMetadataRule
,此问题可以完美解