一、需求描述
最近有个需求,需要动态获取参数,来进一步配置相关的build.gradle配置。比如我们APP,可能同一份代码,编译出不同的apk出来,引用的一些库不一样,一些库对应的key之类的都不一样。
而且APP工程和下面的library工程都需要对此进行相关的判断,一开始我使用过productFlavors来进行这些处理。
参考了下面的链接:
但是总感觉这样在APP工程和对应的子模块都可以采用productFlavors模式的方法很繁琐。
二、实现需求
2.1 APP工程使用productFlavor解决
比如我们需要配置三个productFlavor,如下所示
每一个productFlavor要配置一大堆参数。
但是在子模块中,有些自己写的库,因为不同的apk要使用不同的配置,因此编译出来几个不同版本的aar库,但是这些aar库基本对外的api都是一样的。所以需要通过判断不同的apk,使用不同的aar。
我们一开始尝试的方法是将子模块里面的库,使用compileOnly的方式进行编译即可,然后在APP模块使用具体的版本aar。如下所示
在子模块的 build.gradle中的依赖
在APP工程中 build.gradle中的依赖
在APP工程,因为使用了productFlavor,所以直接改成
格式是 productFlavor名+implement的方式,不同的productFlavor编译依赖不同的aar库。
2.2 APP工程和对应的子模块都可以采用productFlavors模式
我这边就不贴自己工程的代码了,下面内容引用自 https://www.jianshu.com/p/fe39658ca14e
2.2.1 组合多个产品风味
某些情况下,您可能希望组合多个产品风味中的配置。比如像我们app之前就有test环境、预发环境、线上环境,如今又有分app1和app2,那你肯定希望能组合它们,比如组合之后有“app1测试环境debug”、“app1预发环境debug”、“app2预发环境debug”、“app1预发环境release”、“app2预发环境release”等等组合,为此,您可以通过适用于 Gradle 的 Android 插件创建产品风味组,称为风味维度。构建您的应用时,Gradle 会将您定义的每个风味维度中的产品风味配置与构建类型配置组合来创建最终构建变体。Gradle 不会组合属于相同风味维度的产品风味。如下列子说明:
//使用flavorDimensions属性来创建一个“模式”风味维度,维度来区分不同的类型,然后两个类型之间组合
//“dev”指的是编译出来是什么环境的apk
//“app”指的是app1和app2
flavorDimensions "dev","app"
flavorDimensions "dev","app"
productFlavors{
//app2产品,注意(这边的产品名字字母不能大写)
B{
dimension "app"
applicationIdSuffix "com.wmj.b"
versionCode 1
versionName "1.0.0"
}
//app1产品,注意(这边的产品名字字母不能大写)
A{
dimension "app"
applicationId "com.wmj.a"
versionCode 1
versionName "1.0.0"
}
//线上环境
product{
dimension "dev"
}
//测试环境
zeotest{
dimension "dev"
}
//预发环境
yufa{
dimension "dev"
}
}
构建变体:[product, zeotest, yufa][B, A][Debug, Release] 到时这样组合就可以构建12个变体
2.2.2 子模块也可以采用productFlavors模式
目前我们app是采用主工程依赖子模块的架构,一旦与另一个app融合的话,那子模块肯定也是有差异化的东西,所以子模块也是需要支持productFlavors模式,不同gradle的版本有不同的配置:
1.gradle 2.xx的版本
子模块使用productFlavors时必须加上publishNonDefault true属性
主app依赖子模块的时候,由于需要使用到“组合多个产品风味”特点,build.gradle下写的依赖配置需要写很多代码,如下:
configurations {
yufaBReleaseCompile
yufaAReleaseCompile
yufaBDebugCompile
yufaADebugCompile
zeotestBReleaseCompile
zeotestAReleaseCompile
zeotestBDebugCompile
zeotestADebugCompile
productBReleaseCompile
productAReleaseCompile
productBDebugCompile
productADebugCompile
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
yufaAReleaseCompile project(path: ':One', configuration: 'ARelease')
yufaADebugCompile project(path: ':One', configuration: 'ADebug')
yufaBReleaseCompile project(path: ':One', configuration: 'BRelease')
yufaBDebugCompile project(path: ':One', configuration: 'BDebug')
zeotestAReleaseCompile project(path: ':One', configuration: 'ARelease')
zeotestADebugCompile project(path: ':One', configuration: 'ADebug')
zeotestBReleaseCompile project(path: ':One', configuration: 'BRelease')
zeotestBDebugCompile project(path: ':One', configuration: 'BDebug')
productAReleaseCompile project(path: ':One', configuration: 'ARelease')
productADebugCompile project(path: ':One', configuration: 'ADebug')
productBReleaseCompile project(path: ':One', configuration: 'BRelease')
productBDebugCompile project(path: ':One', configuration: 'BDebug')
}
这样之后在BuildVariants下面才有不同的构建变体
2.gradle 3.xx版本
子模块只需要如下配置:
flavorDimensions "app"
productFlavors{
B{
dimension "app"
}
A{
dimension "app"
}
}
然后主app依赖只需要 implementation project(":One")一句话就搞定依赖。
2.2.3 总结
这种方式也是可以实现的,但是感觉需要配置的东西太多,太繁琐,不同的productFlavors和不同的buildTypes配置完后,需要配置的就变成了 m*n 个配置,我不是很喜欢,想想我就只想配置m个配置,需要几个不同的APK 就配置几个。
2.3 在build.gradle中动态获取参数选项
我是想着,总共就三个不同版本的apk,不管是不是在APP工程还是对应的子模块都需要根据不同版本的apk,配置不同的一些配置项,因此我可能需要传递一个参数告知APP工程还是对应的子模块,这样他们就可以在对应的build.gradle里面区分不同的apk,配置不同的配置项了。
2.3.1 如何在build.gradle中动态获取参数选项?
参考了链接 https://www.cnblogs.com/kuliuheng/p/9454280.html
通过 project.hasProperty(‘VERSION_CODE’) 的形式来获取动态传参。
defaultConfig {
applicationId "com.ixwork"
minSdkVersion 15
targetSdkVersion 23
//关键看这两行
versionCode project.hasProperty('VERSION_CODE') ? Integer.parseInt(VERSION_CODE) : DEF_VERSION_CODE
versionName project.hasProperty('VERSION_NAME') ? VERSION_NAME : "${DEF_VERSION_NAME}"
buildConfigField("String", "API_HOST", "${API_DEV_HOST}")
}
2.3.2 怎么传参呢?
通过-PVAR_NAME=VAR_VALUE 的形式,其中-P就是加参数,例如:
gradle clean assembleBeta -PVERSION_CODE=5 -PVERSION_NAME=1.1.1 -POUT_PUT_DIR=/home/user/Desktop -PFILE_NAME=test.apk
// 这里面一个小细节很重要,执行gradle任务时之前加一个clean清除一下结果,
//可以有效防止编译过程中误报错误。比如关闭了某个宏导致某些class文件不再被编译,
//但是可能由于旧的信息残留导致报错提示找不到class
2.3.3 实践一下
2.3.3.1 定义变量
在一个叫 jenkins.gradle 文件中,定义一个变量apkType
,并且定义DEF_APKTYPE_XTC
、DEF_APKTYPE_IMOO
、DEF_APKTYPE_OKII
等三个变量。如下所示,当可以从外面的gradle命令中获取到传入的参数时,则使用参数,如果获取不到就是有默认值DEF_APKTYPE_XTC
/**
* 定义几个变量,在build.gradle里面引用
*/
ext {
DEF_APKTYPE_XTC = "xtc"
DEF_APKTYPE_IMOO = "imoo"
DEF_APKTYPE_OKII = "okii"
//在jenkins或者本地执行 gradlew clean assembleDebug -PAPK_TYPE="okii" 则可以传递参数 APK_TYPE
apkType = project.hasProperty('APK_TYPE') ? APK_TYPE : DEF_APKTYPE_XTC
}
2.3.3.2 应用变量
- 1、在 APP工程还是对应的子模块的build.gradle文件中引入jenkins.gradle文件
apply from: '../jenkins.gradle'
- 2、使用jenkins.gradle文件中定义的变量 apkType
比如在APP工程中的defaultConfig{}配置中,外面就可以判断这个apkType 变量的值,不同的值,配置不同的defaultConfig
如,比如apkType 变量的值 为 定义的默认值 DEF_APKTYPE_IMOO的话,则使用下面的配置
比如apkType 变量的值 为 定义的默认值 DEF_APKTYPE_OKII 的话,则使用下面的配置
在子模块中, 也引入jenkins.gradle文件,然后使用jenkins.gradle文件中定义的变量 apkType 。
如下,不同的APP类型使用不同的aar库。
2.3.3.3 使用gradle命令传递参数
比如我们执行下面的命令,生成一个okii类型的apk出来
gradlew clean assembleDebug -PAPK_TYPE="okii"
执行完毕之后,生成一个okii类型的apk
查看BuildConfig,生成的apkType类型则为 “okii”
因此,我们的参数传递进去生效了,这样我们可以在任何子模块里面都是要这个变量做相关的差异性操作了。
在jenkins中引入
在【构建】 里 加入一个【Execute shell】 然后输入下面的命令即可一次性打包多个apk
# 进入workspace
cd ${WORKSPACE}
./gradlew clean assembleRelease -PAPK_TYPE="xtc" && ./gradlew assembleDebug -PAPK_TYPE="xtc"
但是有时候会因为权限不够,从而导致编译失败,如下所示:
我们只需要给 gradlew 添加一个 可执行权限即可,如下所示
# 进入workspace
cd ${WORKSPACE}
# 添加可执行的权限
chmod +x gradlew
./gradlew clean assembleRelease -PAPK_TYPE="xtc" && ./gradlew assembleDebug -PAPK_TYPE="xtc"
这样就可以正常编译了
作者:欧阳鹏 欢迎转载,与人分享是进步的源泉!
转载请保留原文地址:https://blog.csdn.net/qq446282412/article/details/88988180
☞ 本人QQ: 3024665621
☞ QQ交流群: 123133153
☞ github.com/ouyangpeng
☞ [email protected]