前言
本文作为实战系列,主要讲解gradle在实际项目中的使用,原理部分不会讲解太多。本文会用到一些Groovy的知识,如果不了解Groovy基础语法的话可以参考一下主席的文章Groovy基础,Gradle执行时序。当然我也会对使用到的地方做注解说明,就算你不看的话,也行。
1、统一依赖管理
这里主要是说项目的编译版本、依赖库的版本等,这里参考Google官方的项目,在项目的根目录单独建一个version.gradle文件统一管理版本,这样做的好处明显,特别是多module的项目,很方便的做统一版本管理,避免冲突。但是这样做也会带来一个坏处,就是你inspection检查新版本的时候,当有了新版本的话,你是检查不到的。不过总体来说,利大于弊吧。
来看一看version.gradle文件里都有些什么吧
// version.gradle文件
ext{
android = [
compileSdkVersion: 27,
minSdkVersion : 21,
targetSdkVersion : 27,
versionName: "1.0.0"
]
dependencies = [
appcompatV7 : 'com.android.support:appcompat-v7:27.1.1',
design : 'com.android.support:design:27.1.1',
constrant :'com.android.support.constraint:constraint-layout:1.0.2'
]
}
这里作为说明,只写了几个最基本的依赖,接下来就是要使用这些配置了。首先在project的build.gradle文件里引入
buildscript {
apply from: 'version.gradle'
repositories {
google()
jcenter()
}
…………
}
然后,在module的gradle里就可以愉快的使用了。
compileSdkVersion rootProject.ext.android.compileSdkVersion
defaultConfig {
minSdkVersion rootProject.ext.android.minSdkVersion
targetSdkVersion rootProject.ext.android.targetSdkVersion
versionCode getCode()
versionName rootProject.ext.android.versionName
…………
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation rootProject.ext.dependencies.appcompatV7
implementation rootProject.ext.dependencies.constrant
implementation rootProject.ext.dependencies.design
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}
2、根据versionName 自动生成versionCode
细心的小伙伴可能发现了,上面的versionCode并没有使用version.gradle里配置的versionCode,事实上version.gradle里也并没有配置versionCode,这里我是根据产品需求使用versionName自动生成的versionCode。
// 读取version.gradle里的versionName 生成versionCode
def getCode() {
String name = rootProject.ext.android.versionName
List arr = name.tokenize('.')
int code1
int code2
int code3
code1 = arr.get(0).toInteger() * 10000
code2 = arr.get(1).toInteger() * 100
code3 = arr.get(2).toInteger()
int code = code1 + code2 + code3
}
上面就是获取版本号的方法,采用三段式,每段两位,可以根据实际需求自己改。另外说明一下,Groovy语法里,如果自己不写return语句,会把最后一行计算的code自动return。这样的话,就不用每次版本升级要修改versionName还要修改versionCode了。注意上面的代码是在android的同级领域。
3、优雅的签名设置
签名文件怎么生成这里就不说了,需要说明的是为了方便引用,jks签名文件我放在了app目录下,同样在这个目录下,创建了一个signing.properties的文件,文件内容如下:
// signing.properties 文件
storePass=123456
alias=key
keyPass=123456
v2SigningEnabled=false
signingFile=key.jks
就是基本的签名信息了,看一下module的gradle里的使用:
signingConfigs {
// 定义signConfig并赋值
signConfig
File propFile = file('signing.properties')
if (propFile.exists()) {
Properties props = new Properties()
props.load(new FileInputStream(propFile))
if (props.containsKey('signingFile') && props.containsKey('storePass') &&
props.containsKey('alias') && props.containsKey('keyPass') && props.containsKey("v2SigningEnabled")) {
signConfig.storeFile = file(props['signingFile'])
signConfig.storePassword = props['storePass']
signConfig.keyAlias = props['alias']
signConfig.keyPassword = props['keyPass']
android.signingConfigs.signConfig.v2SigningEnabled = props['v2SigningEnabled']
} else {
android.buildTypes.release.signingConfig = null
android.buildTypes.flavor.signingConfig = null
}
} else {
android.buildTypes.release.signingConfig = null
android.buildTypes.flavor.signingConfig = null
}
print("signingConfigs===========${signConfig}")
}
就是定义一个signConfig 并给赋值了,很简单。但是这样写就避免了直接在gradle里暴露签名信息了。当然你可以在terminal里运行一下gradle guild指令来验证,看一下打印结果是否正确。
配置完以后,直接在buildTypes里引用就行了。
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.signConfig
…………
}
4、自定义BuildConfig
实际开发中服务器一般有正式环境和生成环境,有时候还会有个后台开发人员自己本地的服务器环境,这时候通过自定义buildConfigField来区分配置不同的环境
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.signConfig
buildConfigField "boolean","isRelease","true"
…………
}
我们在release的type里把值设置为true,其他的type设置为fasle,这样,同步完以后,在BuildConfig类里会多出一个静态变量isRelease,这样在java代码里,就可以根据这个变量来判断是测试环境还是生产环境了,还可以根据这个变量来控制是否需要日志输出等。甚至你都可以直接把测试服务器地址和生产服务器地址配置到里面都行。
buildConfigField 'String','API_SERVER_URL','"http://lxy.v1/"'
5、不同的版本配置不同的包名和AppName
实际开发中,为了区分测试版和正式版,有时候需要把测试版的包名和app名字设置为不同。其实这样的需求用gradle分分钟就可以实现
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.signConfig
buildConfigField "boolean","isFlavor","false"
manifestPlaceholders = [
APP_NAME : "release"
]
}
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.signConfig
buildConfigField "boolean","isFlavor","false"
applicationIdSuffix 'debug'
manifestPlaceholders = [
APP_NAME : "debug"
]
}
如上,在debug版里,配置了applicationIdSuffix 'debug',这样打包的话,会在原来的包名后面加上debug,这样安装到手机上就是两个不同的app了,因为包名不同。这样一个测试机就可以同时测试测试包和正式包了。而且,还设置了manifestPlaceholders占位符,这样在menifests文件里直接引用就行,就实现了不同的包名和app名字。
// 清单文件里引用
android:label="${APP_NAME}"
这里有个坑,如果这样配置过后,如果你项目里使用有微信支付、分享等的sdk,注意支付、分享后的回调的类所在的包和正式版分开,否则是收不到回调的(微信的sdk有时候很蛋疼)。
6、自定义打包apk的文件名
在android的领域里添加如下代码
applicationVariants.all { variant ->
variant.outputs.all { output ->
def newApkName
newApkName = getTime() + defaultConfig.versionName + "-" + defaultConfig.versionCode + ".apk"
outputFileName = newApkName
}
}
getTime()方法在android领域的同级
def getTime() {
return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
}
7、java8和dataBinding的支持
java8的lambda还是很简洁的,同样,dataBinding用起来一样爽飞
// 当然在android领域里设置了
dataBinding {
enabled = true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
8、自定义jinlibs目录
现在新建as项目会自动在app目录下生成一个libs目录,可以在gradle里设置jar、so库等的文件目录
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
9、混淆、多渠道打包等其他配置
混淆配置的话配置在release版本里,注意别忘了添加混淆文件
minifyEnabled true
zipAlignEnabled true
shrinkResources true
用gradle打包多渠道的话比较慢,这里就不讲了,个人感觉360多渠道打包挺好用的,推荐一下。
以上是实际项目里常用的配置,当然还有很多不太常用的配置,如可能需要打包aar到maven,gradle编译、gradle性能检测、gradle加速、使用gradle缓存,自定义gradle插件等等,篇幅问题,大家自己学习了……
最后附上完整的demo地址,如果对你有帮助,麻烦start鼓励一下。