前言
gradle统一入口版本管理的初衷是解决一个项目只要一个地方修改,各个有引用的模块便可以自动生效,从而达到了一个地方修改各个地方生效的好处,同时能保持gradle引用库版本的一致,特别适合在多module项目中统一管理。
如何新建一个config.gradle文件
先说明一下我是Mac环境和Window有那么一点区别,但是基本差不多。在根目录新建config.gradle文件, 并在项目的app\build.gradle 中引用它。
config.gradle中的配置
图中已经标注了步骤 ,按照上述步骤操作,你的全局gradle就已经配置好了,接下来看一下具体是怎么配置的。
//config.gradle,ext:添加额外的属性
ext {
//基础构建属性
android = [
compileSdkVersion : 30, // 编译sdk版本
buildToolsVersion :"30.0.1", //指定SDK构建工具的命令行版本,这个属性是可选的
applicationId :'com.example.myapplication', //唯一标识要发布的包。
minSdkVersion : 19, //最低安装版本
targetSdkVersion : 30, //目标适配版本
versionCode : 1,
versionName : "1.0.0",
ndkVersion : "21.0.6113669" //ndk版本配置
]
//远程依赖版本
dependent_Version = [
appcompat_version: "1.3.0",
]
//远程依赖
dependencies = [
"appcompat" : "androidx.appcompat:appcompat:${dependent_Version["appcompat_version"]}",
"appcompat-resources" : "androidx.appcompat:appcompat-resources:${dependent_Version["appcompat_version"]}",
"material" : 'com.google.android.material:material:1.1.0',
"constraintlayout" : 'androidx.constraintlayout:constraintlayout:2.0.4',
"navigation-fragment" : 'androidx.navigation:navigation-fragment:2.2.2',
"navigation" : 'androidx.navigation:navigation-ui:2.2.2',
"okio" : 'com.squareup.okio:okio:2.2.2',
"okhttp" : 'com.squareup.okhttp3:okhttp:3.12.1',
//集成 Bugly
"crashreport" : 'com.tencent.bugly:crashreport:latest.release',
"nativecrashreport" : 'com.tencent.bugly:nativecrashreport:latest.release',
]
//签名配置
sign=[
keyAlias :'admin',
keyPassword :'admin',
storeFile :"/Users/Project/admin.keystore",
storePassword: 'admin',
]
}
针对上面的config.gradle配置做一下说明,基本构建属性部分没什么可说的,常用的属性都已经填写上了。远程库依赖部分,我单独拆出去一个远程库版本,这样写的目的是为了统一相同版本号依赖好管理。
例如上面展示的appcompat库和appcompat-resources库。建议有同版本的库统一依赖到dependent_Version[],不同版本正常写到dependencies[]
根目录/build.gradle配置
//把配置好的全局文件加载进来,如果不加上这个配置app/build.gradle无法使用配置好的全局属性
apply from:"config.gradle"
buildscript {
repositories { //配置远程仓库
google()
jcenter()
}
dependencies { //配置构建工具
classpath "com.android.tools.build:gradle:4.1.2"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
/**
* 运行gradle clean时,执行此处定义的task任务。
*/
task clean(type: Delete) {
delete rootProject.buildDir
}
app/build.gradle配置
app/build.gradle先发一份完整的配置,可供大家参考。然后按顺序把相关的配置拆出来单独进行讲解说明
apply plugin: 'com.android.application' //表示该模块为应用程序模块,可以直接运行,打包得到的是.apk文件
//'com.android.library',表示该模块为库模块,只能作为代码库依附于别的应用程序模块来运行,打包得到的是.aar文件
def buildConfig=rootProject.ext.android
/**
* 定义时间类型格式,精确到毫秒
*/
def buildTime() {
return new Date().format("yyyyMMddHHmmss", TimeZone.getTimeZone("GMT+08:00"))
}
android {
compileSdkVersion buildConfig.compileSdkVersion
buildToolsVersion rootProject.ext.android.buildToolsVersion
useLibrary 'org.apache.http.legacy' //HttpClient在Android5.0上无效,在Studio里面,可以通过使用useLibrary 'org.apache.http.legacy'配置进行编译
defaultConfig { //默认基本配置项
applicationId rootProject.ext.android.applicationId
minSdkVersion rootProject.ext.android.minSdkVersion
targetSdkVersion rootProject.ext.android.targetSdkVersion
versionCode rootProject.ext.android.versionCode
versionName rootProject.ext.android.versionName
ndkVersion rootProject.ext.android.ndkVersion
multiDexEnabled true //多dex的支持
flavorDimensions "default"
// 全局清单占位符配置
manifestPlaceholders = [
'UMENG_APPKEY' : '999888',
]
//resValue "string", "facebook_app_id", "12sasdvg"
}
signingConfigs{ //签名配置
debug { //开发环境
keyAlias rootProject.ext.sign.keyAlias
keyPassword rootProject.ext.sign.keyPassword
storeFile file(rootProject.ext.sign.storeFile)
storePassword rootProject.ext.sign.storePassword
}
release{ //正式环境
keyAlias rootProject.ext.sign.keyAlias
keyPassword rootProject.ext.sign.keyPassword
storeFile file(rootProject.ext.sign.storeFile)
storePassword rootProject.ext.sign.storePassword
}
configAll {
keyAlias rootProject.ext.sign.keyAlias
keyPassword rootProject.ext.sign.keyPassword
storeFile file(rootProject.ext.sign.storeFile)
storePassword rootProject.ext.sign.storePassword
}
}
buildTypes { // 构建项目类型
debug {
debuggable true // 是否支持断点调试
jniDebuggable true //是否可以调试NDK代码
zipAlignEnabled false // 压缩对齐开关
shrinkResources false // 移除无用的资源
minifyEnabled false //是否开启代码混淆
signingConfig signingConfigs.debug // 设置签名信息
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'//指定混淆的规则文件
buildConfigField('boolean', 'LOG_SHOW', 'true') // 日志打印开关
buildConfigField('String', 'BUGLY_APPID', '"0def765219"') //bugly key
ndk {
abiFilters 'armeabi-v7a'
}
// debug版本清单占位符配置
// manifestPlaceholders = [
// 'UMENG_APPKEY' : '111222',
// ]
resValue "string", "facebook_app_id", "aaabbb"
}
release {
debuggable false // 是否支持断点调试
jniDebuggable false //是否可以调试NDK代码
zipAlignEnabled true // 压缩对齐开关
shrinkResources true // 移除无用的资源
minifyEnabled true //是否开启代码混淆
signingConfig signingConfigs.release // 签名信息配置
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'//指定混淆的规则文件
buildConfigField('boolean', 'LOG_SHOW', 'false') // 日志打印开关
buildConfigField('String', 'BUGLY_APPID', '"0def711122s2"') //bugly key
ndk {
// armeabi:万金油架构平台(占用率:0%)
// armeabi-v7a:曾经主流的架构平台(占用率:10%)
// arm64-v8a:目前主流架构平台(占用率:95%)
abiFilters 'armeabi-v7a', 'arm64-v8a'
}
// release版本清单占位符配置
// manifestPlaceholders = [
// 'UMENG_APPKEY' : '2223333',
// ]
resValue "string", "facebook_app_id", "bbbccc"
}
}
productFlavors {
Google {
}
FaceBook {
}
Twitter {
}
}
/**
* 该属性多用于第三方库文件重复或者冲突
*/
packagingOptions {
exclude 'META-INF/*******' //剔除这个包下制定目录下的所有文件(不会移除签名信息)
doNotStrip '*/mips/*.so' //剔除mips下的所有SO架构
pickFirst "lib/mips/libil2cpp.so" //匹配到多个相同文件,只提取第一个
merge 'META-INF/LICENSE' //当出现重复文件时 合并重复的文件 然后打包入apk
}
compileOptions { //编译选项
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
aaptOptions { //aapt打包工具,会映射到com.android.build.gradle.internal.dsl.aaptOptions类
noCompress '.unity3d', '.ress', '.resource', '.obb' //不对左侧文件类型压缩
ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~" //忽略文件类型
}
lintOptions { //lint选项
checkReleaseBuilds false //表示在进行Release构建时不再进行Lint检查
abortOnError false //检查到错误后继续编译,不取消当前的构建任务。
}
sourceSets {//目录指向配置
main {
jniLibs.srcDirs = ['libs']//指定lib库目录
}
}
/**
* 重新命名apk名称 格式:帝国游戏_v1_20210616112133_debug.apk
* 命名规范 应用名-版本号-毫秒时间-开发环境.apk
*/
android.applicationVariants.all {
variant ->
variant.outputs.all {
outputFileName = "帝国游戏_"+"v${variant.versionCode}_"+"${buildTime()}_"+variant.name+".apk"
}
}
/**
* 重新格式重命名aar文件 格式:lib_plugin_bytedance-debug_20210422130607.aar
* 这个只能在 apply plugin: 'com.android.library' build.gradle中添加,不然报错
*/
// android.libraryVariants.all { variant ->
// variant.outputs.all {
// outputFileName = "lib_plugin_bytedance-"+ variant.name+"_${buildTime()}.aar"
// }
// }
}
dependencies {
// 依赖本地 libs 目录下所有的 jar 和 aar 包
implementation fileTree(include: ["*.jar", '*.aar'], dir: "libs")
implementation rootProject.ext.dependencies["appcompat"]
// print("获取到信息:"+rootProject.ext.dependencies["appcompat"])
implementation rootProject.ext.dependencies["appcompat-resources"]
implementation rootProject.ext.dependencies["material"]
implementation rootProject.ext.dependencies["constraintlayout"]
implementation rootProject.ext.dependencies["navigation-fragment"]
implementation rootProject.ext.dependencies["navigation"]
//集成 Bugly
implementation rootProject.ext.dependencies["crashreport"] //其中latest.release指代最新Bugly SDK版本号,也可以指定明确的版本号,例如2.1.9
implementation rootProject.ext.dependencies["nativecrashreport"] //其中latest.release指代最新Bugly NDK版本号,也可以指定明确的版本号,例如3.0
}
基本配置项
配置好config.gradle 后,开始引用一下全局gradle的属性。rootProject.ext.android等同于取config.gradle配置里的ext属性下的android字段。上面定义了一个buildConfig变量是向大家演示一下另一种写法,在compileSdkVersion 属性中做了展示。
至于目的吗?看着那么长一串不爽,简化一下,同学们可根据自己的喜好进行编写,结果是一样的就是写法不同而已。接下来的全局远程依赖部分和全局签名也是同理不做说明。基本配置部分没什么好说的,就这样我继续往下说。
signingConfigs属性配置
这部分是配置一些签名模式的 ,其中包含默认的debug(开发测试)和release(对外发布)两种模式。在这两个基础上添加了一个configAll的签名模式。
我先说一下默认的两种模式,从规范角度出发,开发中基本会有测试版本和发布版本,它们分别对应一个签名,方便管理和区分。
但有另外一种情况,例如我们接入Google Play,这个SDK有点特别 !!!怎么特别呢? 它是绑定包名和签名文件的。包名不对、签名文件MD5等信息对不上,支付都拉不起来。
假如你运行debug模式,用的是系统默认的签名(非Google后台记录的签名),那么恭喜你支付一定是拉不起来的。针对这种情况增加了第三个属性配置, configAll 就是为了处理这种情况。通俗的说configAll属性就是debug和release共用一个签名(针对google情况)。
//默认情况下的config.gradle 签名配置
sign=[
keyAlias :'admin',
keyPassword :'admin',
storeFile :"/Users/Project/admin.keystore",
storePassword: 'admin',
]
观察一下,下面的写法与最开始的签名配置有很大的不同。先说一下config.gradle部分,增加了一个新的属性signConfigs,它的加入是为了configAll属性而生。
也就是debug和release共用这个签名,这个签名取自sign字段里面的属性集合,很灵活。只要把需要的配置都加上,随你取签名属性集。例如configAll要上架Google 写法就可以改成:signConfigs=sign["google"]
//改版本后的config.gradle 签名配置
sign=[
"google" :[
keyAlias :'admin1',
keyPassword :'admin1',
storeFile :"/Users/Project/admin1.keystore",
storePassword: 'admin1',
],
"bytedance":[
keyAlias :'admin2',
keyPassword :'admin2',
storeFile :"/Users/Project/admin2.keystore",
storePassword: 'admin2',
],
]
//获取固定签名属性配置
signConfigs=sign["bytedance"]
下面代码主要说debug和release 模式,两种模式都分别配置了不同的签名。这个配置偏向于,debug模式签名做测试。正式模式用release签名。大家可以根据自己的需求做一下调整,写法上不局限于SDK。
// app/build.gradle
signingConfigs{ //签名配置
debug { //开发环境
keyAlias rootProject.ext.sign["bytedance"]["keyAlias"]
print("keyAlias:========== "+rootProject.ext.sign["bytedance"]["keyAlias"]+" && ")
keyPassword rootProject.ext.sign["bytedance"]["keyPassword"]
print("keyPassword:========== "+rootProject.ext.sign["bytedance"]["keyAlias"]+" && ")
storeFile file(rootProject.ext.sign["bytedance"]["storeFile"])
print("debug签名文件路径:========== "+rootProject.ext.sign["bytedance"]["storeFile"]+" && ")
storePassword rootProject.ext.sign["bytedance"]["storePassword"]
print("storePassword:=========="+rootProject.ext.sign["bytedance"]["storePassword"]+" && ")
}
release{ //正式环境
keyAlias rootProject.ext.sign["google"]["keyAlias"]
print("keyAlias:========== "+rootProject.ext.sign["google"]["keyAlias"]+" && ")
keyPassword rootProject.ext.sign["google"]["keyPassword"]
print("keyPassword:========== "+rootProject.ext.sign["google"]["keyAlias"]+" && ")
storeFile file(rootProject.ext.sign["google"]["storeFile"])
print("release 签名文件路径:========== "+rootProject.ext.sign["google"]["storeFile"]+" && ")
storePassword rootProject.ext.sign["google"]["storePassword"]
print("storePassword:=========="+rootProject.ext.sign["google"]["storePassword"]+" && ")
}
configAll { //通用
keyAlias rootProject.ext.signConfigs.keyAlias
keyPassword rootProject.ext.signConfigs.keyPassword
storeFile file(rootProject.ext.signConfigs.storeFile)
storePassword rootProject.ext.signConfigs.storePassword
}
}
buildTypes构建类型
这部分主要说默认的两种模式,debug模式主要用于代码和日志的分析,不会发布到线上给用户使用,配置的属性偏向于排查 和定位作用。release是要发布线上给用户使用,所以属性配置的很正式,关闭掉了无用的信息。
注释比较齐全不做多余说明。建议例如签名,测试环境用signingConfigs.debug 正式环境用signingConfigs.release,直观、清晰明了。
介绍一下buildConfigField属性,它会在运行时创建debug或者release的BuildConfig.java 用法很灵活,例如debug需要打印日志,release需要关闭日志 。
LOG_SHOW就是app/build.gradle 里的buildConfigField('boolean', 'LOG_SHOW', 'true') 属性配置,debug模式等于true,release等于false。下面代码做一下逻辑区分,避免了重要日志输出到线上,供不法分子做不利的事情。
public class LogUtils {
public static boolean isShowLog= BuildConfig.LOG_SHOW;
public static void info(String tag, String message) {
if (isShowLog) {
Log.i(tag, message);
}
}
}
再来介绍一下 manifestPlaceholders 属性配置,简单说就是对清单文件做配置。开发中很多时候我们会遇到这种场景,比如说:在用到一个第三方sdk,但是这个sdk并没有区分开发环境和线上环境,这时候我们就可能会申请两个不同的key标识,而且很多key标识都只能在androidmanifest里面配置。
所以每次上线生成apk就必须手动去更改key标识,如果渠道版本少还好,如果多了是个很要命的事情。拿友盟SDK举个例子,集成友盟sdk 在清单文件一定会配置meta-data属性,用下面的写法你会发现配置不同环境的参数,在不同环境展示的也不同。这种方式避免了频繁手动改key值,一次配置好一劳永逸。方便快捷效率还高。
如果所有模式共用一个友盟KEY 建议配置默认defaultConfig{}属性内。manifestPlaceholders属性不止友盟sdk这种需求,不同环境更改应用名字同样适用。我就不做举例,学会举一反三很重要。
buildTypes
debug {
manifestPlaceholders = [
// debug版本清单占位符配置
'UMENG_APPKEY' : '111222',
]
}
release {
manifestPlaceholders = [
// debug版本清单占位符配置
'UMENG_APPKEY' : '2223333',
]
}
//清单文件配置
接着介绍一下resValue属性,这个属性跟manifestPlaceholders类似,主要是对res/values/Strings.xml做处理的。如果不想在Strings.xml直接写属性,可以通过resValue方式添加进去。可以在不同维度上做改变,特别灵活。用法如下:
buildTypes
debug {
resValue "string", "facebook_app_id", "aaabbb"
}
release {
resValue "string", "facebook_app_id", "bbbccc"
}
我只把release配置展示出来了,debug 得到的字符串是 aaabbb,这里我不做演示了。 如果全局共用一个resValue属性建议写在默认defaultConfig{ resValue "string", "facebook_app_id", "zzzaaaa"}属性内,在任意模式下 得到的字符串都是zzzaaaa
productFlavors变体配置
这里做个简单描述,加上配置后会生成多个apk,例如生成Google包可以在google{}属性里面做配置,配合着debug和release 加上、上面说过的属性自由组合,会有意想不到效果。这productFlavors变体属性和buildTypes属性类似。
APK重新命名
做一下简单说明:相比传统默认的debug.apk 或者release.apk方式命名,重新命名带好的好处直观明了,多个包的看着不乱也规范,可以给我们开发中减少不少麻烦。
如何用: 这个属性只能在apply plugin: 'com.android.application' 中使用,执行gradle命令或者build项目都可以生成对应的文件。
/**
* 重新命名apk名称 格式:帝国游戏_v1_20210616112133_debug.apk
* 命名规范 应用名-版本号-毫秒时间-开发环境.apk
*/
android.applicationVariants.all {
variant ->
variant.outputs.all {
outputFileName = "帝国游戏_"+"v${variant.versionCode}_"+"${buildTime()}_"+variant.name+".apk"
}
}
AAR重新命名
如何用: 这个属性只能在apply plugin: 'com.android.library' 中使用,执行gradle命令或者build项目都可以生成对应的文件。
// 重新格式重命名aar文件 格式:lib_plugin_bytedance-debug_20210422130607.aar
android.libraryVariants.all {
variant ->
variant.outputs.all {
outputFileName = "lib_plugin_bytedance-"+ variant.name+"_${buildTime()}.aar"
}
}
文章末尾
本文偏向于记录作者工作中用到的技术部分,那一次忘了会以笔记的形式回顾。内容可能略微啰嗦,偏向基础和 详细说明。各位看官多多见谅 !!!