关于Gradle

Posted by Csming on 2018-09-16

什么是Gradle

Gradle是基于Apache Ant和Apache Maven概念的自动化构建工具。他使用一种基于Groovy的DSL来声明项目设置。

Gradle能够:

自动处理包相依关系(取自 Maven Repos 的概念)
自动处理布署问题(取自 Ant 的概念)
条件判断写法直觉(使用 Groovy 语言)

Gradle的编译过程

Gradle中有两个对象:Project、Task。

每个项目的编译至少有一个Project,一个build.gradle就代表一个project,每个project里面包含了多个task,task里包含了多格action。action包含了需要被执行的代码。

编译过程中,Gradle会根据build相关文件,聚合所有的project和task,执行task中的action。

执行顺序由依赖逻辑来保证。所有的task都需要依赖其他task来执行,没有被依赖的task首先被执行。

编译过程分为:

1.初始化阶段: 创建project对象。

2.配置阶段: 执行所有的编译脚本,同事创建project的所有task。

3.gradle根据传入的参数决定如何执行这些task。

Gradle Files

setting.gradle 用于定义加入编译过程的module

顶层的build.gradle 最终被应用到所有项目中

顶层的build.gradle

buildscript: 定义了Android编译工具的类路径。

allprojects: 定义的属性会被应用到所有module中。

每个项目的build.gradle

1.apply plugin: 定义了gradle插件。

eg:

1
apply plugin "com.android.application"

2.android: 用于配置android的所有特殊配置。这个是前年声明的plugin提供的

(1)defaultConfig: 程序的默认配置。

(2)buildType: 定义了编译类型。可以针对每个类型进行不同的配置。(release。debug等)

3.dependencies: 是gradle的依赖配置。定义了当前项目需要依赖的其他库。

Gradle Wrapper

gradle wrapper用于处理对以往项目的兼容性问题。

Gradle basics

Gradle会根据build文件的配置生成不同的task。我们可以直接单独执行每个task。

可以使用./gradlew task列出所有的task。如果想要列出每个task对应依赖的其他task,可以使用./gradlew task -all

Android tasks

基本的四个task:

1.assemble: 对所有的buildType 生成apk包
2.clean: 移除所有的编译输出文件,比如apk包
3.check: 执行lint检测编译
4.build: 同时执行assemble和check命令

Configuration

BuildConfig

可以在buildTypes中配置一些key-value对。他们会体现在BuildConfig中

repositories

Repositories: 就是代码仓库,我们平时的添加的一些 dependency就是从这里下载的,Gradle 支持三种类型的仓库:Maven,Ivy和一些静态文件或者文件夹。

在执行阶段,gradle会从仓库中取出对应需要的依赖文件。

除了使用远程库以外,还可以使用相对路径配置本地仓库:

1
2
3
4
5
repositorys {
flatDir {
dirs "aars"
}
}

Local dependencies

File dependencies

通过files()方法可以添加文件依赖,也可以通过fileTree()添加一个文件夹:

1
2
3
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}

Native libraries

配置本地.so库:

1
2
3
4
5
android {
sourceSets.main{
jniLibs.srcDir 'src/main/libs'
}
}

Library projects

如果要写一个library项目让其他项目引用,则需要使用一下plugin:

1
apply plugin: 'com.android.library'

Build Variants

编译时动态根据当前的编译类型输出不用样式的apk文件。这时候就需要使用buildType了

BuildType

Source Sets

每当创建新的build type的时候,gradle默认会创建一个信息source set。我们可以建立与main文件夹同级的文件夹,根据编译类型的不同可以选择对某些源码直接进行替换。

Product flavors

前面build type的做法能够对同一份源码编译同一个程序的不同类型,如果我们需要针对同一份源码编译不同的程序,就需要用到Product flavors;

所有的product flavor版本和defaultConfig共享所有属性

和build type一样,product flavor也有自己的source set文件夹。除此之外,build type和product flavor可以结合,他们的文件夹里面的文件优先级甚至高于单独的build type和product flavor文件夹的优先级。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
android {
flavorDimensions "color", "price"

productFlavors {
red {
flavorDimension "color"
}

blue {
flavorDimension "color"
}
free {
flavorDimension "price"
}

paid {
flavvorDimension "price"
}
}
}

Signing configurations

签名相关。

当我们打包市场版时,我们需要输入keystore数据,如果是debug版本系统会帮我们配置这些信息。这些信息在gradle中都配置在signingConfigs中:

1
2
3
4
5
6
7
8
9
10
11
12
android {
signingConfigs {
staging.initWith(signingConfigs.debug)

release {
storeFile file("release.keystore")
storePassword "secretpassword"
keyAlias "gradleforandroid"
keyPassword "secretpassword"
}
}
}

配置之后可以再build type中直接使用

1
2
3
4
5
6
7
android {
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}

Optimize

Speeding up multimodule builds

可以通过以下方式加快gradle编译:

开启并行编译: 在项目根目录下的gradle.properties中设置

1
org.gradle.parallel=true

开启编译守护进程: 该进程在第一次启动后会一直存在,当第二次编译的时候,可以重用该进程。

1
org.gradle.daemon=true

加大可用编译内存:

1
org.gradle.jvmargs=-Xms256m -Xmx1024m

Reducing apk file

在编译时,我们可能会有很多资源并没有用到,此时可以通过shrinkResources来优化我们的资源文件,除去不必要的资源

1
2
3
4
5
6
7
8
android {
buildTypes {
release {
minifyEnabled = true
shrinkResources = true
}
}
}

某些情况下,一些资源需要通过动态加载的方式载入,这时候就需要在res/raw/下简历一个keep.xml文件,通过以下方式keep资源:

1
2
3
4
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"
tools:discard="@layout/unused2" />

Manual shrinking

对于一些特殊文件或者文件夹,例如国际化资源文件、屏幕适配资源等。如果我们已经确定过了某种幸好,而不需要重新适配,就可以去掉不可能被适配的资源:

1
2
3
4
5
android{
defaultConfig {
resConfigs "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"
}
}

Profiling

当我们执行所有task的时候,都可以添加–profile参数生成一份执行报告在reports/profile中。

Practice

在开发中,我们可能需要自己自定义task。这就需要用到Groovy了。

Groovy

Groovy我打算专门去学习一下。这里就不记录了。

通过hook Android编译插件重命名apk

1
2
3
4
5
6
7
android.applicationVariants.all {
variant.outputs.each { output ->
def file = output.outputFile

output.outputFile = new File(file.parent, file.name.replace(".apk", "-${variant.versionName}.apk"))
}
}

学习资料来自:https://www.jianshu.com/p/9df3c3b6067a