Android Gradle配置问题
实际项目开发过程中,可能遇到以下问题:
- 应用存在多套环境(测试、运维、正式);
- 应用需要在多个渠道上发布;
- 不同环境的apk可以安装在同一个手机上(方便测试同学测试);
- 不同环境的apk可以拥有不同的应用名称(方便测试同学测试);
- 不同渠道的apk可以拥有不同的资源文件(如某些渠道的启动页不同);
- 不同环境引用不同的常量;
Gradle中签名文件的处理
签名文件涉及到应用的隐私,不能直接采用共有的仓库进行管理,可以采用配置文件的形式来处理
在项目根目录下新建一个文件:signing.properties(文件名称随意)
打开signing.properties文件,添加如下内容
1
2
3
4
5
6
7
8# 签名文件路径
KEY_STORE_PATH=./demo.keystore
# 密码
KEY_STORE_PASSWORD=demo
# Alias
KEY_STROE_ALIAS=demo.keystore
# key 密码
KEY_PASSWORD=demo注意:上面假设项目根目录下存在一个demo.keystore的文件,且其他信息如上所示
打开项目的主module(一般为app module),在其build.gradle文件的添加如下内容(注意:添加位置:在android节点外)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22# 定义加载签名信息的方法
def loadSignProperties() {
File propFile = file('../signing.properties')
if (propFile.exists()) {
Properties props = new Properties()
props.load(new FileInputStream(propFile))
if (props.containsKey('KEY_STORE_PATH') && props.containsKey('KEY_STORE_PASSWORD') &&
props.containsKey('KEY_STROE_ALIAS') && props.containsKey('KEY_PASSWORD')) {
println 'signing.properties exist, and all props is here'
android.signingConfigs.release.storeFile = file(props['KEY_STORE_PATH'])
android.signingConfigs.release.storePassword = props['KEY_STORE_PASSWORD']
android.signingConfigs.release.keyAlias = props['KEY_STROE_ALIAS']
android.signingConfigs.release.keyPassword = props['KEY_PASSWORD']
} else {
println 'signing.properties exist, but some props is missing'
android.buildTypes.release.signingConfig = null
}
} else {
println 'signing.properties not exist'
android.buildTypes.release.signingConfig = null
}
}调用上面方法加载签名信息
1
2# 注意该方法调用的实际
loadSignProperties()注意:
- loadSignProperties()这个方法的调用必须在Android节点之后,因为方法里面使用了Android节点定义的属性,不然会报如下错误:
Could not get unknown property 'release' for SigningConfig container
File propFile = file('../signing.properties')
这里制定签名配置文件的路径时,由于配置文件和当前文件不在同一个目录下,先要采用’..’ 回到根目录,在利用’/‘在当前文件中查找该配置文件
- loadSignProperties()这个方法的调用必须在Android节点之后,因为方法里面使用了Android节点定义的属性,不然会报如下错误:
使用buildConfigField来定义常量的问题
在主module(一般只appmodule)的buildTypes节点中,可以在不同的编译类型如debug、release中制定buildConfigField的不同值,然后在编译后生成的BuildConfig来使用这个值,这给针对不同环境采用不同配置提供了可能,如debug中开启日志,release中关闭日志等,十分便利,但利用BuildConfig一定要充分保证其安全性。参考如下文章:
Android中使用BuildConfig.DEBUG必须知道的内幕
上述文章的结论:主module对library module的依赖都是release依赖,所以,如果你在library module中也采用了BuildConfig的话,他的值就一直对应的时release里面定义的那个值
解决方案
在library module的build.gradle中添加如下代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15android {
......
......
productFlavors {
create('all') {
}
}
publishNonDefault true
}
configurations {
allDebug
allRelease
}文章中提到添加上述代码,但是添加后发现会报错,实际上只需要添加如下代码即可:
1
2
3
4
5
6
7
8
9
10
11
12android {
......
......
productFlavors {
}
publishNonDefault true
}
configurations {
allDebug
allRelease
}library module中的buildType要和主module中一致
改变主module对library的依赖方式
1
2
3
4
5
6
7
8
9
10
11
12dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:24.2.1'
//依赖library
debugCompile project(path: ':library', configuration: 'allDebug')
releaseCompile project(path: ':library', configuration: 'allRelease')
//省略其余依赖
.....
.....
}其实,还要加上如下依赖:
1
compile project(path: ':library')
不然你会发现在使用library的地方都会引用不到library中相关的类
解决上面的问题后,就放心大胆的添加buildConfigField了
gradle 3.0重命名生成的apk文件和改变输出路径
重命名文件
3.0之前的做法:
1
2
3
4
5
6
7
8
9
10
11
12
13applicationVariants.all { variant ->
if (variant.buildType.name == "release") {
variant.outputs.all { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.apk')) {
// apk_渠道名-版本号-版本名称-编译时间.apk
def fileName = "demo-${defaultConfig.versionCode}- v${defaultConfig.versionName}-${releaseTime()}.apk"
output.outputFile = new File(outputFile.getParent(),fileName)
//
}
}
}
}3.0之后的做法:
1
2
3
4
5
6
7
8
9
10
11
12
13applicationVariants.all { variant ->
if (variant.buildType.name == "release") {
variant.outputs.all { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.apk')) {
// apk_渠道名-版本号-版本名称-编译时间.apk
def fileName = "demo-${defaultConfig.versionCode}- v${defaultConfig.versionName}-${releaseTime()}.apk"
output.outputFileName = fileName
//
}
}
}
}更改输出路径
输出路径如果不更改,就是在主module的build文件夹的apk目录下。
3.0之前做法:
直接在重命名的时候指定其输出路径,如:
将
output.outputFile = new File(outputFile.getParent(),fileName)
改为:output.outputFile = new File(distinationDir,fileName)
3.0之后做法:(参考文章:3.0之后修改apk输出路径
由于output.outputFile 属性变为只读,需采用如下方式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24applicationVariants.all { variant ->
//这个修改输出的APK路径
variant.getPackageApplication().outputDirectory = new File(project.rootDir.absolutePath + "/apk")
// 修改文件名方法一:
variant.outputs.all { output->
def outputFile = output.outputFile
if(outputFile != null && outputFile.name.endsWith('.apk')) {
def fileName = "demo-${defaultConfig.versionCode}- v${defaultConfig.versionName}-${releaseTime()}.apk"
output.outputFileName = fileName
}
}
//修改文件名方法二:
variant.getPackageApplication().outputScope.apkDatas.forEach { apkData ->
apkData.outputFileName = "AppName-" +
variant.versionName + "_" +
apk_time + "_" +
variant.flavorName + "_" +
variant.buildType.name + "_" +
variant.signingConfig.name +
".apk"
}
}注意:这里修改输出文件的名字时,如果在每个buildType中单独设置,那么编译出的文件名就是最后定义的buildType中修改的名称,所以我在3.0之后修改apk名的时候,实在Android节点添加上述代码,然后再根据buildType.name 来对不同的buildType做区分处理。
带着问题去学习?
如何在Android Studio中查看某个gradle版本(如gradle:3.4.3)源码?
将主项目下面
build.gradle
文件dependencies
下的gradle插件版本声明文件,复制到主module的build.gradle
文件的dependencies
下,并将前面的classpath
声明改为implemention
,然后同步项目即可
1 | classpath 'com.android.tools.build:gradle:3.4.3' |
调用 apply plugin: 'com.android.application'
发生了什么?
升级项目至AndroidX,gradle.properties会添加如下配置,这些配置到底是如何工作的?
1 | # Android 插件会使用对应的 AndroidX 库而非支持库。 |
[迁移AndroidX实践及Jetifier源码分析]https://yuweiguocn.github.io/migrate-to-androidx/