Gradle基础
Gradle的生命周期分为三个不同的阶段:初始化 -》 配置 -》 构建
初始化:settings.gradle
配置:build.gradle
构建:gradle
初始化阶段会读取根目录下setting.gradle的include信息,决定哪些工程会加入构建过程,并且创建project实例。
配置阶段会按引用树去执行所有工程的build.gradle脚本,配置project对象,一个对象由多个任务组成。
运行阶段会根据Gradle命令传递过来的Task名称,执行相关依赖任务。
版本参数优化
每个module的build.gradle文件都拥有一些必要的属性,同一个Android工程中,在不同的模块中要求这些属性一致,如果不一致会造成资源 的重复,另一方面会降低编译效率。那么就必须有一个统一的、基础的gradle配置。
资源引用配置
1 使用sourceSets的方式来指定文件的路径。
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']
}
}
2 可以动态添加res资源,在buildtype 和productFlavor中定义resValue变量
resValue "string","app_name","xxx"
resValue只能动态添加资源,无法替换资源。如果资源名重复,Gradle会提示重复定义资源。
3可以指定特定尺寸的资源
resConfigs "hdpi","xhdpi","xxhdpi"
4 通过build.gradle编译生成BuildConfig文件,可以直接让代码读取到BuildConfig中的值。
buildConfigField("String","BASE_URL","\"https://xxx.com.cn\"")
在buildConfig文件中就会得到:
public final class BuildConfig {
public static final boolean DEBUG = Boolean.parseBoolean("true");
public static final String APPLICATION_ID = "";
public static final String BUILD_TYPE = "debug";
public static final String FLAVOR = "";
public static final int VERSION_CODE = 10101;
public static final String VERSION_NAME = "1.01.01";
// Fields from build type: debug
public static final String BASE_URL = "https://xxx.com.cn";
}
Gradle加载的优先级
BuildType -> productFlavor -> Main -> dependencies
最左边的优先级越高
Build types
Gradle在Android中的build type是用来处理app或者library应该被构建成什么类型,在这个配置中,我们可以定义应用的包名是什么,是否自动去除掉没有引用的资源,是否开启混淆等等。
buildTypes {
release {
//设置正式包名称
applicationVariants.all { variant ->
variant.outputs.all { output ->
project.ext { appName = 'xxx' }
def newName = project.ext.appName +"-" + defaultConfig.versionName + "-" +buildTime() + ".apk"
outputFileName = new File(newName)
}
}
//开启混淆
minifyEnabled true
//Zipalign优化
zipAlignEnabled true
//移除无用的resource文件
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
debug {
minifyEnabled false
shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.debug
applicationVariants.all { variant ->
variant.outputs.all { output ->
project.ext { appName = 'XXX' }
def newName = project.ext.appName +"-" + defaultConfig.versionName + "-" +buildTime() + ".apk"
outputFileName = new File(newName)
}
}
}
Product flavors
与build type相反,build type用来构建不同类型的app,debug版或release版,而produc flavors则是用来在同一个app上创建不同的版本,如付费版和免费版。一个非常常见的应用场景就是我们创建了一个银行管理的app给不同银行提供服务,但是不同的银行App的logo不一样,有produc flavors就能在基于一套代码上创建不同版本的app或者library。
productFlavors {
red {
applicationId 'com.gradleforandroid.red'
versionCode 3
}
blue {
applicationId 'com.gradleforandroid.blue'
minSdkVersion 14
versionCode 4
}
}
Multiflavor variants
在某些情况下,我们可能需要进行flavors的组合,比方说你的app有两套主题,绿色主题和红色主题,然后有两个版本,付费版和免费版,你可能需要进行组合,类似于红色免费版,红色付费版,绿色免费版,绿色付费版。通过使用flavorDimensions可以解决flavors组合的问题,
flavorDimensions "color", "price"
productFlavors {
red {
flavorDimension "color"
}
blue {
flavorDimension "color"
}
free {
flavorDimension "price"
}
paid {
flavorDimension "price"
}
}
Variant filters
在某些情况下,可能我们不想使用某种build variant,例如现在有debug和release的build type,red和blue的flavors,但是,blue还是测试环境根本现在用不到blue的release版本,那么我们可以直接过滤掉,在android studio中的buildVariants窗口就不会出现了,过滤可以在app模块或者library模块的build.gradle文件中加入如下代码:
android.variantFilter { variant ->
if (variant.buildType.name.equals('release')) {
variant.getFlavors().each() { flavor ->
if (flavor.name.equals('blue')) {
variant.setIgnore(true);
}
}
}
}
gradle的依赖特性
1 implementation
会将指定的依赖添加到编译路径,并且会将该依赖打包到输出,如apk中,但是这个依赖在编译时不能暴露给其他模块,例如依赖此模块的其他模块。这种方式指定的依赖在编译时只能在当前模块中访问。
例如:
有两个模块app 和test,模块app依赖test,test添加了远程二进制库依赖joda-time,在test模块中使用没有问题,在app模块中使用则报错。
2 api
使用api配置的依赖会将对应的依赖添加到编译路径,并将依赖打包输出,但是这个依赖是可以传递的,比如模块A依赖模块B,B依赖库C,模块B在编译时能够访问到库C,但是与implemetation不同的是,在模块A中库C也是可以访问的。
3 compileOnly
compileOnly修饰的依赖会添加到编译路径中,但是不会打包到apk中,因此只能在编译时访问,且compileOnly修饰的依赖不会传递
4 runtimeOnly
这个与compileOnly相反,它修饰的依赖不会添加到编译路径中,但是被打包到apk中,运行时使用。
5 annotationProcessor
用于注解处理器的依赖配置,像第三方库 ButterKnife
Gradle的一些配置
开启Gradle的守护进程来构建项目:
org.gradle.daemon=true
如果你要构建一个多Module并且依赖关系比较复杂的项目,那么你可以使用并行项目执行:
org.gradle.parallel=true
在Gradle主目录中配置的属性优先级高于在项目中配置的属性。当你并不想一个个项目的去改动配置时,可以定义一份常用的Gradle配置文件放在Gradle的主目录下
对于内存较小的机器避免Gradle编译卡顿
修改gradle.properties,避免影响其它人编译速度,把修改的gradle.properties文件放到用户文件夹.gradle下
#设置最大堆内存为1.5g和最大非堆内存0.5g
org.gradle.jvmargs=-Xmx1536m -XX\:MaxPermSize\=512m -XX\:+HeapDumpOnOutOfMemoryError -Dfile.encoding\=UTF-8
#不启用守护进程
org.gradle.daemon=false
#不启用并行编译
org.gradle.parallel=false
#使用构建缓存
android.enableBuildCache=true
修改build.gradle文件,如下:
dexOptions {
//最大堆内存
javaMaxHeapSize "2g" // 1g should be also OK
}
压缩Apk大小
加快Build速度
Gradle的Android插件在 buildType 上有一个名为 minifyEnabled 的布尔属性,我们需要将其设置为true以启用ProGuard:
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
压缩Resource文件
自动压缩的方式很简单,就是在构建中配置 shrinkResources 属性。如果将此属性设置为true,Android构建工具将自动检测哪些资源未被使用,并且不会将它们包括在APK中。
android {
buildTypes {
release {
minifyEnabled = true
shrinkResources = true
}
}
}
使用 resConfigs 属性配置要保留的资源,然后其余的将被抛出。
defaultConfig {
resConfigs "en", "da", "nl"
resConfigs "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"
}
忽略Lint检查
当使用Gradle执行构建时,Android构建任务将会对代码执行Lint检查。Lint是一个静态代码分析工具,用于标记布局和Java代码中的潜在错误。在某些情况下,Lint一旦报错,构建就会停止。如果之前没有在项目中使用Lint,并且我们想迁移到Gradle,Lint可能会出现很多错误。为了使构建工作不会因为Lint检查而中断,你可以配置Gradle来忽略Lint错误,并通过禁用 abortOnError 来阻止它们中止构建。这只是一个临时解决方案,因为忽略Lint检查可能会产生像丢失翻译类似这样的潜在错误,这可能会导致应用程序崩溃的问题。为了防止Lint阻塞构建过程,可以像下面这样禁用abortOnError:
android {
lintOptions {
abortOnError false
}
}