最近在做项目制的开发,有一套开发好的产品原型的代码,然后根据不同的客户进行不同的定制开发。如果一个项目搞一个分支,要多开多个as,还有一个缺点就是如果产品原型的代码进行了变更和优化,所有的项目分支都要将产品修改后果的代码复制转移一次,麻烦!
build.gradle可以帮我们解决这个问题。
一.先来讲重点,配置变种:
1.在android { } 中增加如下代码,适用于as建立的项目,eclipse转换过来的项目需要增加sourceSets {}
// 维度组合,productFlavors中不同种类的dimension可以组合,名字根据实际需要自己取
flavorDimensions "mode", "channel"
productFlavors {
trunk {
// 维度小组名称
dimension "mode"
// 修改项目包名,在清单文件中的包名后增加后缀;
// 也可以使用applicationId "com.xx.xx" 替换掉默认的包名
applicationIdSuffix ".trunk"
// 编译后会自动生成在BuildConfig.java中,代码中通过使用BuildConfig.IPADRESS获取后面的值
// 参数规则“String”表示参数类型,也可以是boolean等,"IPADRESS"是常量名称,最后一个是内容
buildConfigField "String", "IPADRESS", "\"192.168.x.xx:8080\""
// 还可以增加各种不同的配置,defaultConfig中配置这里都可以用,重复的配置这里的会覆盖掉defaultConfig
}
guiyang {
dimension "mode"
applicationIdSuffix ".guiyang"
buildConfigField "String", "IPADRESS", "\"192.168.x.xx:8082\""
}
huawei{
dimension "channel"
// 给清单文件中的渠道占位符赋值
manifestPlaceholders = [CHANNEL_VALUE:"huawei"]
}
xiaomi{
dimension "channel"
manifestPlaceholders = [CHANNEL_VALUE:"xiaomi"]
}
2.在工程目录中新增如下目录
如果productFlavors中没有main:main为默认目录,每个组合都会含有main中的代码;
如果productFlavors中有main:main也相当于一个风味,不会和trunk合并(根据实际需要,trunk既可以是main的扩展也可以和main平级,完全看自己怎么配置)
trunk和guiyang(注意:文件夹名称要和productFlavors中的名字对应)中放各个定制项目中的差异代码,其下的目录结构文件非必须,如果需要修改main中的资源或新增代码,就在各自项目中增加对应的文件夹和文件,如果没有修改可以只有个主目录
trunk/guiyang和main中的文件合并规则如下:
图片、音频、 XML 类型的 Drawable ,layout等资源文件,将会进行文件级的覆盖
字符串、颜色值、整型等资源以及 AndroidManifest.xml ,将会进行元素级的覆盖
代码资源,同一个类, buildTypes 、 productFlavors 、 main 中只能存在一次,否则会有类重复的错误
覆盖等级为:buildTypes > productFlavors > main
3.配置后在as左下角Build Variants中可以看到如下组合,维度组合2*2,每个组合再分debug和release,共8种
选中哪一个就会编译哪一个,维度数量根据自己需要可以减少,也可以增加
4.依赖库也可以根据风味进行不同的依赖
编译用的第三方库。例如我们的app在某些情况下有地图功能,有些情况下没有,如果我们在代码里控制有没有这个功能那么它依赖的包还是会打入apk,这样就会造成大量无用的代码进入apk中。
现在不同的变种风味有不同的依赖,dependencies {}中可以这样写:
compile 'xxx' // 所有的项目都依赖
trunkCompile 'yyy' // 只有trunk项目依赖
trunkCompile 'zzz' // 只有trunk项目依赖
guiyangCompile 'yyy' // 只有guiyang项目依赖
implementation也可以这样用
5.eclipse转换过来的项目,需要多一步
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
}
trunk {
java.srcDirs = ['src-trunk']
res.srcDirs = ['res-trunk']
}
guiyang{
java.srcDirs = ['src-guiyang']
res.srcDirs = ['res-guiyang']
}
}
sourceSets中主要指定代码目录,因为eclipse的目录结构和as不一样,as自动识别main下面的代码,需要通过sourceSets将eclipse的目录和as的目录进行对应
as中如果有特殊需要,也可以通过这种方式指定目录
二.重点讲完了,下面记一下build.gradle中各个配置的说明
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
// 默认配置,还有很多配置属性,参照gradle API
// 相当于所有productFlavor的父类,自建productFlavor中没有的都使用这个里面的,有的就覆盖
defaultConfig {
applicationId "com.example.xxx.gradledemo"
minSdkVersion 14
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
// 签名的配置
// productFlavor的属性,在所有的productFlavor都可以使用,一般用在buildTypes中,覆盖登记最高
signingConfigs {
realse { // 这个名字随意取,在buildTypes中通过signingConfigs. 调用
storeFile file("gradle_demo.jks")
storePassword "xxxxxx"
keyAlias "xxx"
keyPassword "xxxxxx"
}
}
// 相当于一个维度dimension(隐藏有一个debug),和productFlavors中的风味进行组合
buildTypes {
// 也是一个productFlavor
// 一般增加这个风味是为了不同的签名需要,还有很多配置属性,参照gradle API
// release,debug中的buildConfigField会覆盖productFlavors中同名字的
release {
signingConfig signingConfigs.realse
buildConfigField "boolean", "ISSHOW", "false"
// 是否减小apk体积,打开混淆
minifyEnabled true
// 混淆文件的配置文件,getDefaultProguardFile('proguard-android.txt')是默认的,在SDK的tools/proguard目录下可以找到
// proguard-rules.pro是自定义
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
// 设置java的编译版本,通常是为了使用某些版本中的一些语言新特性
// 一般不用设置
/*compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}*/
// 编译时加速,lint检查有错误时继续打包,不影响使用,最好是把lint错误全部修改
// 还有很多配置属性,参照gradle API
/*lintOptions {
abortOnError false // 如果发现错误,lint是否停止打包
}*/
// 维度组合,productFlavors中不同种类的dimension可以组合,名字根据实际需要自己取
flavorDimensions "mode", "channel"
productFlavors {
// 一个productFlavor
trunk {
// 维度小组名称
dimension "mode"
// 修改项目包名,在清单文件中的包名后增加后缀;
// 也可以使用applicationId "com.xx.xx" 替换掉默认的包名
applicationIdSuffix ".trunk"
// 编译后会自动生成在BuildConfig.java中,代码中通过使用BuildConfig.IPADRESS获取后面的值
// 参数规则“String”表示参数类型,也可以是boolean等,"IPADRESS"是常量名称,最后一个是内容
buildConfigField "String", "IPADRESS", "\"192.168.3.23:8080\""
// 还可以增加各种不同的配置,defaultConfig中配置这里都可以用,重复的配置这里的会覆盖掉defaultConfig
}
guiyang {
dimension "mode"
applicationIdSuffix ".guiyang"
buildConfigField "String", "IPADRESS", "\"192.168.8.47:8082\""
}
huawei{
dimension "channel"
// 给清单文件中的渠道占位符赋值
manifestPlaceholders = [CHANNEL_VALUE:"huawei"]
}
xiaomi{
dimension "channel"
manifestPlaceholders = [CHANNEL_VALUE:"xiaomi"]
}
}
/*sourceSets {
trunk {
res.srcDirs = ['src/trunk/res']
manifest.srcFile 'src/trunk/AndroidManifest.xml'
}
guiyang {
res.srcDirs = ['src/guiyang/res']
manifest.srcFile 'src/guiyang/AndroidManifest.xml'
}
}*/
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
buildConfigField配置的数据在BuildConfig.java中,由as自动生成
在这个目录下面
三.build.gradle中的一些参数使用公用资源文件,主要是一些编译版本和jar包版本,modle和主工程都要用到的需要统一的
1.可以在根目录的build.gradle文件中增加ext{ },或者新建一个.gradle文件,然后引入到根目录的build.gradle
根目录下build.gradle 增加
ext {
CompileSdkVersion = 27
BuildToolsVersion = "27.0.3"
MinSdkVersion = 14
TargetSdkVersion = 27
}
主工程和modle的build.gradle中这样使用
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
applicationId "com.example.xxx.gradledemo"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
或者在任意地方新建xxx.gradle,然后引入到根目录下build.gradle(在根目录中增加 apply from : 'xxx.gradle',需要根据xxx.gradle路径写)
ext{
// 可以直接用
/*compileSdkVersion = 27
buildToolsVersion = "27.0.3"
minSdkVersion = 14
targetSdkVersion = 27*/
// 也可以外面套一层
android = [
compileSdkVersion : 27,
buildToolsVersion : "27.0.3",
minSdkVersion : 14,
targetSdkVersion : 27
]
dependencies = [
appcompatV7 : 'com.android.support:appcompat-v7:27.1.1',
constraintVersion : 'com.android.support.constraint:constraint-layout:1.1.3'
]
}
主工程和modle的build.gradle中这样使用
android {
compileSdkVersion rootProject.ext.android.compileSdkVersion
buildToolsVersion rootProject.ext.android.buildToolsVersion
defaultConfig {
applicationId "com.example.zhangchi.gradledemo"
minSdkVersion rootProject.ext.android.minSdkVersion
targetSdkVersion rootProject.ext.android.targetSdkVersion
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation rootProject.ext.dependencies.appcompatV7
implementation rootProject.ext.dependencies.constraintVersion
testImplementation 'junit:junit:4.12'
}
}
2.可以在gradle.properties中增加(一般放敏感信息,比如打包用的签名信息)
在gradle.properties中添加敏感数据
#签名文件写实际路径
STOREFILE=../gradle_demo.jks
STOREPASSWORD=xxxxxx
KEYALIAS=xxx
KEYPASSWORD=xxxxxx
主工程的build.gradle中这样使用
realse {
storeFile file(STOREFILE)
storePassword STOREPASSWORD
keyAlias KEYALIAS
keyPassword KEYPASSWORD
}
四.gradle命令打包
1.Gradle配置(配不配置都行)
下载地址http://services.gradle.org/distributions/
下载你所需要的gradle版本,带all的,gradle-4.5-all.zip
下载后解压到你想要的目录
设置环境变量
新建系统变量:GRADLE_HOME,值为grdle路径
设置后测试是否配置成功
在as的Terminal工具栏中:
使用gradlew命令使用的是项目使用的gradle版本,使用gradle命令使用的是环境变量中配的gradle版本
多变种使用命令打包的命令:gradlew assembleGuiyangHuaweiRelease
gradle assemble维度组合名称(每个风味首字母大写)buildType下面的名字(首字母大写)
注:本篇博客纯属个人笔记,如果错误之处,还望帮忙指正,谢谢!
如果想了解的更详细,可以读一本书籍《Android Gradle权威指南》