速速了解Gradle构建工具

一句话概述,Gradle 就是一个运行在 JVM 上的自动化的项目构建工具,用来帮助我们自动构建项目。

对于开发者来说,Gradle 的主要作用主要有 3 个:

  1. 项目构建:提供标准的、跨平台的自动化项目构建方式。
  2. 依赖管理:方便快捷的管理项目依赖的资源(jar 包),避免资源间的版本冲突问题。
  3. 统一开发结构:提供标准的、统一的项目结构。

不知道从什么时候开始,现在用列表就会感觉自己是AI(汗)


Gradle是一个现代化的构建自动化工具,首次发布于2007年。它的出现是为了应对当时构建工具的局限性,尤其是Ant和Maven等工具在灵活性和可扩展性方面的不足。Gradle的设计理念是结合了这两者的优点,同时引入了更强大的功能和更灵活的构建模型。

最初,Gradle用于Java项目的构建。它采用了一种基于Groovy的领域特定语言(DSL),使得构建脚本的编写更加简洁和易于理解。Gradle的灵活性使得它能够支持多种编程语言和平台,包括但不限于Java、Groovy、Kotlin、Scala、Android等。随着时间的推移,Gradle逐渐成为Android开发的标准构建工具,Android Studio也将其作为默认构建系统。

Groovy是一种基于Java平台的动态语言,旨在提高Java开发的生产力。它于2003年首次发布,作为一种简化Java编程的语言,Groovy结合了面向对象编程和动态编程的特性,允许开发者以更简洁和灵活的方式编写代码。

Gradle的核心特性之一是其增量构建能力,这意味着它只会重新构建那些发生变化的部分,从而提高构建效率。此外,Gradle还支持多项目构建,允许开发者在一个构建中管理多个模块和项目,这在大型应用开发中尤为重要。在构建工具(如Gradle)、Web开发(如Grails框架)和测试(如Spock框架)等多个领域中得到了广泛使用。

可以看看github仓库:Gradle GitHub Repository

gradle配置文件组成

在Gradle中,构建过程的基本组成部分包括Project和Task。每个待构建的工程被称为Project,而构建一个Project所需执行的一系列操作则被称为Task。通过这些Task,Gradle能够完成从编译到打包等一系列构建过程。

Project与Task

每个Project可以包含多个Task,这些Task代表了构建过程中的不同步骤。例如,在构建一个Android应用的过程中,可能会涉及以下Task:

  • Java源码编译:将Java源代码编译成字节码。
  • 资源文件编译:处理XML、图片等资源文件。
  • Lint检查:对代码进行静态分析,检查潜在问题。
  • 打包:将编译后的代码和资源打包成最终的APK文件。

以下是一个相对完整的示例,展示了如何在build.gradle文件中定义Task,作用直接以注释的形式标准了,这里不再重复介绍:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// 定义一个简单的Java项目
apply plugin: 'java'

// 定义一个Task,用于编译Java源文件
task compileJava {
doLast {
println 'Compiling Java source files...'
// 这里可以添加实际的编译逻辑,例如调用javac命令
}
}

// 定义一个Task,用于运行单元测试
task runTests {
dependsOn compileJava // 依赖于compileJava任务
doLast {
println 'Running unit tests...'
// 这里可以添加实际的测试逻辑,例如调用JUnit
}
}

// 定义一个Task,用于打包应用
task packageApp {
dependsOn runTests // 依赖于runTests任务
doLast {
println 'Packaging the application into a JAR file...'
// 这里可以添加实际的打包逻辑,例如调用jar命令
}
}

// 定义一个Task,用于清理构建输出
task clean {
doLast {
println 'Cleaning up build outputs...'
// 这里可以添加实际的清理逻辑,例如删除生成的文件
}
}

// 定义一个顶级任务,用于构建整个项目
task build {
dependsOn packageApp // 依赖于packageApp任务
doLast {
println 'Building the project...'
}
}

// 定义一个任务,用于执行所有任务的顺序
task fullBuild {
dependsOn clean, build // 先执行clean,再执行build
doLast {
println 'Full build process completed!'
}
}

插件

Gradle的插件系统是其强大功能的核心。插件的主要作用是定义和执行Task。为了使Gradle能够正常工作并完成构建流程,必须导入合适的插件。这些插件中定义了构建Project所需的一系列Task,并负责执行相应的Task。

在Android项目中,通常在app模块的build.gradle文件的第一行应用插件,例如:

1
apply plugin: 'com.android.application'

这行代码的意思是应用“com.android.application”插件来构建app模块。这个插件负责定义并执行Java源码编译、资源文件编译、打包等一系列Task。

在“com.android.application”插件中,定义了以下四个顶级任务:

  • assemble:构建项目的输出(APK文件)。
  • check:进行校验工作,确保代码质量和构建的有效性。
  • build:执行assemble任务和check任务,通常用于完整的构建过程。
  • clean:清除项目的输出,删除生成的APK文件和其他构建产物。

当我们执行一个任务时,Gradle会自动处理任务之间的依赖关系。例如,执行assemble任务时,Gradle会自动执行assembleDebugassembleRelease任务,因为一个Android项目通常需要生成debug和release两个版本的APK。

任务依赖示例

以下是一个示例,展示了如何定义任务及其依赖关系:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
task compileJava {
doLast {
println 'Compiling Java source files...'
}
}

task packageApp(dependsOn: compileJava) {
doLast {
println 'Packaging the application...'
}
}

task build(dependsOn: packageApp) {
doLast {
println 'Building the project...'
}
}

在这个示例中,定义了三个任务:compileJavapackageAppbuildpackageApp任务依赖于compileJava任务,而build任务依赖于packageApp任务。当执行build任务时,Gradle会自动执行packageAppcompileJava任务,确保构建过程的顺序。

任务的执行

在命令行中,可以通过以下命令执行特定的任务:

1
./gradlew build

这条命令会触发build任务,Gradle会自动处理所有依赖的任务并按顺序执行。

分析xposed模块的模块级build.gradle

不必多说,直接上代码即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
plugins {
// 应用Android应用插件,启用Android构建功能
alias(libs.plugins.android.application)
}

android {
// 定义应用的命名空间,通常与包名相同
namespace 'com.xposed.demo.hookdemo'
// 设置编译SDK版本为34
compileSdk 34

defaultConfig {
// 应用的唯一标识符,通常是反向域名格式
applicationId "com.xposed.demo.hookdemo"
// 设置应用支持的最低SDK版本为24(Android 7.0)
minSdk 24
// 设置应用的目标SDK版本为34
targetSdk 34
// 应用的版本代码,整数值,通常在每次发布时递增
versionCode 1
// 应用的版本名称,通常是用户可见的字符串
versionName "1.0"

// 设置测试运行器,用于运行Android测试
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
// 定义构建类型的配置
release {
// 在发布版本中禁用代码混淆
minifyEnabled false
// 指定ProGuard配置文件,使用默认的优化文件和自定义的规则文件
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
// 可以在这里添加其他构建类型,例如debug
}

compileOptions {
// 设置源代码兼容性为Java 11
sourceCompatibility JavaVersion.VERSION_11
// 设置目标代码兼容性为Java 11
targetCompatibility JavaVersion.VERSION_11
}
}

dependencies {
// 添加AndroidX AppCompat库作为依赖,提供向后兼容的功能
implementation libs.appcompat
// 添加Material Design库作为依赖,提供Material组件
implementation libs.material
// 添加JUnit库用于单元测试
testImplementation libs.junit
// 添加扩展JUnit库用于Android测试
androidTestImplementation libs.ext.junit
// 添加Espresso库用于UI测试
androidTestImplementation libs.espresso.core

// 添加Xposed API作为编译时依赖,不会打包到APK中
compileOnly 'de.robv.android.xposed:api:82'
}

然后这里再了解下各个模块的作用(其实根据模块名就可以理解了,这里多唠唠):

  • **android { }**:这个块用于定义Android项目的构建配置。所有与Android构建相关的设置都在这里进行配置。
  • **defaultConfig { }**:在android块内,defaultConfig用于定义应用的基本配置。
  • **buildTypes { }**:在android块内,buildTypes用于定义不同构建类型的配置。
  • **compileOptions { }**:在android块内,compileOptions用于设置Java编译选项。
  • **dependencies { }**:用于定义项目的依赖项,指定所需的库和框架。

常见的gradle配置文件

这里以Android java项目为例进行介绍。

Android项目通常会有两个build.gradle文件:一个是项目级别的build.gradle,另一个是模块级别的build.gradle。项目级别的build.gradle用于配置整个项目的构建设置,包括所有模块的依赖和构建脚本,而模块级别的build.gradle则专注于特定模块的构建配置,如应用的SDK版本、依赖项和构建类型等。

1. build.gradle (Module级别)

这个文件定义了特定模块的构建配置,包括应用的SDK版本、依赖项、构建类型等。每个模块(如app模块)都有自己的build.gradle文件,用于指定该模块的构建规则和依赖关系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
plugins {
id 'com.android.application'
}

android {
namespace 'com.example.app'
compileSdk 34

defaultConfig {
applicationId "com.example.app"
minSdk 21
targetSdk 34
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
}

dependencies {
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

2. build.gradle (项目级别)

这个文件用于配置整个项目的构建设置,包括所有模块的依赖和构建脚本。它通常包含Gradle插件的版本和全局的仓库配置。

1
2
3
4
// Top-level build file where you can add configuration options common to all sub-projects/modules.  
plugins {
alias(libs.plugins.android.application) apply false
}

3. gradle.properties

这个文件用于定义Gradle构建的全局属性和配置选项,例如JVM参数、AndroidX支持等。它可以帮助简化build.gradle文件中的配置。

1
2
3
4
5
6
7
8
# Gradle properties
org.gradle.jvmargs=-Xmx2048m
android.useAndroidX=true
android.enableJetifier=true
keystoreFile=path/to/keystore.jks
keyAlias=myKeyAlias
keyPassword=myKeyPassword
storePassword=myStorePassword

4. gradlew (Linux/Mac)

这是Gradle Wrapper的脚本文件,允许用户在没有全局安装Gradle的情况下运行Gradle命令。它确保使用项目中指定的Gradle版本。

1
2
3
4
5
6
7
#!/usr/bin/env sh
# Gradle wrapper script for Unix
GRADLE_VERSION=7.0.4
if [ -z "$GRADLE_HOME" ]; then
GRADLE_HOME="$PWD/gradle-$GRADLE_VERSION"
fi
exec "$GRADLE_HOME/bin/gradle" "$@"

5. gradlew.bat (Windows)

这是Windows系统下的Gradle Wrapper批处理文件,功能与gradlew相同,允许在没有全局安装Gradle的情况下运行Gradle命令。

1
2
3
4
5
6
7
8
@echo off
setlocal
set GRADLE_VERSION=7.0.4
if "%GRADLE_HOME%"=="" (
set GRADLE_HOME=%~dp0gradle-%GRADLE_VERSION%
)
"%GRADLE_HOME%\bin\gradle" %*
endlocal

6. local.properties

这个文件用于定义本地属性,例如Android SDK的路径。它通常在本地开发环境中使用,确保Gradle能够找到SDK。这个文件不应被版本控制,因为它包含特定于开发者的配置。

1
sdk.dir=/Users/username/Library/Android/sdk

7. settings.gradle

这个文件用于定义项目中包含的模块。它允许Gradle一次性构建所有模块,适用于多模块项目。

1
2
3
include ':app'
include ':ModuleA'
include ':ModuleB'

参考文档