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
 15- android { 
 ......
 ......
 productFlavors {
 create('all') {
 }
 }
 publishNonDefault true
 }
 configurations {
 allDebug
 allRelease
 }- 文章中提到添加上述代码,但是添加后发现会报错,实际上只需要添加如下代码即可: - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12- android { 
 ......
 ......
 productFlavors {
 }
 publishNonDefault true
 }
 configurations {
 allDebug
 allRelease
 }
- library module中的buildType要和主module中一致 
- 改变主module对library的依赖方式 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12- dependencies { 
 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
 13- applicationVariants.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
 13- applicationVariants.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
 24- applicationVariants.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/