1 Gradle的一些概念
Android Studio默认的构建工具为Gradle。在使用Gradle的过程中会碰到一些概念,理清他们的含义关系对我们使用和深入理解Gradle至关重要。
1.1 Gradle 、AGP(Android Gradle Plugin)、 buildTools分别是什么,他们之间什么关系?
1.1.1 Gradle
Gradle是基于JVM的构建工具。他本身使用jave写的,gradle的脚本也就是build.gradle通常是用groovy语言。
1.1.2 Android BuildTools
Android SDK Build-Tools 是构建 Android apk、AAR文件等Android平台产物所需的一个 Android SDK 组件,安装在 <sdk>/build-tools/ 目录下。
//build-tools/
├── ⋮
├── aapt2
├── d8
├── apksigner
├── zipalign
├── ⋮
-
AAPT2:Android 资源打包工具)是一种构建工具,Android Studio 和 Android Gradle 插件使用它来编译和打包应用的。AAPT2 会解析资源、为资源编制索引,并将资源编译为针对 Android 平台进行过优化的二进制格式。
-
apksigner:工具为 APK 签名,并确保 APK 的签名将在该 APK 支持的所有版本 Android 平台上成功通过验证。
-
d8:Android Gradle 插件使用该工具来将项目的 Java 字节码编译为在 Android 设备上运行的 DEX 字节码。
-
Zipalign:在将 APK 文件分发给最终用户之前,先使用 zipalign 进行优化。
1.1.3 Android Gradle Plugin
是基于Gradle的插件,Android Gradle 主要就是调用[Android BuildTools]这个包里的工具实现编译打包Apk等功能。
注:AGP3.0.0或更高版本,插件会使用默认版本的build Tools无需指定。
android { buildToolsVersion "33.0.1"}
1.2 gradleWrapper、gradle-user-home、GradleHome
1.2.1 GradleWrapper
项目的根目录的gradle/wrapper下:
/gradle/wrapper/
├── gradle-wrapper.jar
└── gradle-wrapper.properties
负责下载管理特定版本的gradle,确保不同环境中项目运行在相同个gradle版本里,还能达到复用效果。
-
gradlewraper目录下的gradle.jar负载下载gradle。
-
gradle-wrapper.properties内指定gradle的版本,下载地址,本地位置等配置信息。
1.2.1 GRADLE-USER-HOME
gradle-user-home
├── caches // modules-2和jars-3缓存依赖和产物,且各个版本各个项目中共用
│ ├── jars-3
| ├── transforms-3
│ └── modules-2
├── wrapper //存放不同版本的gradle
│ └── dists
│ ├── ⋮
│ └── gradle-7.4-bin
| └── xxxxxxxx
| └──gradle-7.3.3 //其实这是gradle home路径
└── gradle.properties
默认在位置位于/.gradle。这个目录下包含用户自定义的设置、maven本地仓库、gradle wrapper版本、gradle插件和一些缓存。其中该路径下caches文件夹有repo缓存也就是项目的依赖缓存,这个缓存各个项目是公用的,提升编译速度。
1.2.3 Gradle Home
gradle-home
├── bin
│ ├── gradle.bat
│ └── gradle
├── init.d
│ ├──..
│ └── xx.gradle
└── lib
├──...
├── gradle-wrapper-7.3.3.jar
├── gradle-tooling-api-7.3.3.jar
├──...
└── plugins
├── ...
├── gradle-publish-7.3.3.jar
├── commons-codec-1.15.jar
└──...
其实就是gradle的安装目录,里面主要包括Gradle 的可执行文件、库文件、插件等,gradle执行时会自动加载使用安装目录内的文件插件。
2 Gradle生命周期
Gradle执行构建是有生命周期的,分三个阶段初始化阶段(Initialization Phases),配置阶段(Configuration Phases)和执行阶段(Execution Phases)。
2.1 初始化阶段
- 执行init.gradle脚本:init.gradel脚本可以放置在gradle-user-home目录下、gradle-user-home/init.d/和gradle-home/init.d/目录下,其中后两者下的脚本名称只要一.gralde结尾即可。他们的执行顺序为依次执行。在脚本里我们可以设置一些个性化的配置,如配置repo源、鉴权等。
- 执行setting脚本:gradle会嗅探项目里的settings文件,settings文件中定义哪些项目参与到构建中。在settings.gradle文件中可以设置一些对所有参与的构建的项目做配置,也可以有针对性的做配置。
- 创建project实例:为参与到构建中的项目创建project实例。我们在项目的build.gradle脚本里使用的project对象就是在这个时机创建的。
2.2 配置阶段
- 应用插件:下载应用声明的插件,脚本里有两种写法一种是apply plugin和plugins{}。他们的区别暂不作展开。
- 配置属性:给插件做参数配置,我们最熟悉的android{}闭包其实就是对AGP里的com.android.application插件做参数配置。
- 应用依赖:gradle脚本中声明使用的依赖也会在配置阶段下载应用。
- 构建有向无环task任务图。
*注意:*执行任何task都会走初始化阶段和配置阶段
2.3 执行阶段
执行上一个阶段也就是配置阶段构造好的有向无环图任务。
tasks.register('test') {
println '该语句会在配置阶段执行'
doLast {
println '该语句会在执行阶段运行'
}
}
3 监听Gradle生命周期回调
3.1 监听初始化阶段回调
初始化阶段主要有两个能接收回调的地方settingEvaluated{}和 gradle.projectsLoaded {}。
//settings.gradle配置执行完成
gradle.settingsEvaluated {
...
}
settingEvaluated{}setting创建加载成功。在此之前有几项gradle有几项重要的工作已经完成。
-
gradle.properties 加载完成。
-
init脚本执行完成。
-
Settings对象创建成功,并且从gradle.properties加载的值也注入到了settings中。
// 所有 Project 对象创建(注意:此时 build.gradle 中的配置代码还未执行)
gradle.projectsLoaded {
...
}
初始化阶段还有扫描项目下的所有包含位于根project和子project中的build.gradle并对应生成Project对象,对象创建完成之后就会调用gradle.projectsLoaded{},但注意此时build.gradle 中的配置代码还未执行。
3.2 配置阶段的回调
配置阶段会对项目下的每个build.gradle进行配置,如上图,beforeProject{}/project.beforeEvaluated{}和afterProject{}/after.beforeEvaluate会执行多次,这取决于构建项目下有几个project。
-
beforeProject{}/project.beforeEvaluated{}:会在每个project配置之前调用。
-
afterProject{}/after.beforeEvaluate:会在build.gradle脚本"从头跑到尾"之后调用。
-
当所有的gradle.build都执行完毕后,会调用projectEvaluted{}。
-
最后,task图构建完成后,回调gradle.taskGraph.whenReady {}和gradle.taskGraph.taskExecutionGraphListener.graphPopulated()。
3.3 hook执行阶段
每个task执行前后,我们也有方式去监听:主要是使用gradle.taskGraph.beforeTask{} 和gradle.taskGraph.afterTask {},如下:
gradle.taskGraph.beforeTask { Task task ->
println "executing $task ..."
}
gradle.taskGraph.afterTask { Task task, TaskState state ->
if (state.failure) {
println "FAILED"
}
else {
println "done"
}
}
-
!!!注意: 这些方法在gradle8.0已经直接被移除掉了统统不能用,原因简单的说就是影响cofigurationCache具体看这里: task_execution_events。
https://docs.gradle.org/current/userguide/upgrading_version_7.html#task_execution_events
-
替代的方式:使用build_service。
https://docs.gradle.org/current/userguide/build_services.html#concurrent_access_to_the_service
4 Gradle项目结构
root-project
├── buildSrc
| ├── ...
│ └── build.gradle
├── sub-project
│ └── build.gradle
├── build.gradle
├── settings.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew(Unix Shell script)
└── gradlew.bat(Windows batch file)
4.1 buildSrc
-
buildSrc是位于gradle项目根目录的特殊目录,我们可以在这编写Gradle的插件和任务。
-
这里面的代码会在gardle的配置阶段执行。
4.2 gradle.Properties
-
在gradle-user-home目录下,项目根目录下和子项目目录下(和build.gradle同级)下都可以存在。
-
gradle.properties定义的值有些时配置gradle环境属性,也可以添加自己自定义的属性,在我们的构建脚本中可以直接访问这些值。
//gradle.properties中
mVersionName=1.12.1
//build.gradle
println("mVersion name in gradle.properties ${mVersionName}")
4.3 build.gradle和settings.gradle 文件
Gradle世界里经过初始化阶段后,build.gradle文件会对应生成Poject对象,settings.gradle文件会生成Settings对象,其实还有Gradle对象,每次构建启动时就会创建。
5 plugin 与 task
5.1 Gradle Plugin
Gradle是一个构建框架,本身不具备特别强的构建能力,具体构建能力依托于Gradle Plugin ,比如要打包APK就要依托AGP。AGP 内部通过调用Android Build Tool 去具体执行诸如编译class,打包资源等任务。
5.1.1 Apply plugin方式引入
Gradle Plugin的是使用首先是引入,就跟我们在项目里需要某个第三方库需要引入一样。在跟目录的build.gradle中就可声明这些依赖,然后在构建脚本里声明应用,然后做配置。
//in rootproject/build.gradle
buildscript {
...
dependencies {
classpath:'com.android.tools.build:gradle:x.x.x'
}
}
//in subProject/build.gradle
apply plugin: 'com.android.application'//用于打包生成Apk
android {//构建Apk的配置
}
//in subProject2/build.gradle
apply plugin: 'com.android.library'//用于打包产出AAR
android{ //构建产生aar配置
}
如果在脚本gradle脚本中想使用一个第三方库,也需要通过classpath引入,然后就可以在脚本中使用了,示例如下。
//in root project build.gradle
buildscript {
dependencies {
classpath: 'com.squareup.okhttp3:okhttp:3.14.9'
}
}
import okhttp3.OkHttpClient
task("checkClasspath").doFirst {
//在脚本中使用引入了的okhttp
def client = new OkHttpClient()
..,
}
5.1.2 plugin{}脚本块引入
以上这种使用plugin的方式是分两步【引入+应用】,gradle还有引入和应用一步搞定的方式。
plugin{ id 'com.android.library' version 'x.x.x' }
默认的这种不需要引入就能应用插件的方式,插件来源只能是gradle 插件官网,如果想使用自己的插件需要在pluginManagement中配置源如下:
https://plugins.gradle.org/
//settings.gradle
pluginManagement {
repositories {
maven {
url './maven-repo'
}
}
}
5.1.3 其他
Gradel插件分两种:二进制插件和脚本插件;二进制插件就是被打包成了jar包的插件。脚本插件.gradle文件可以被作为插件使用。这种插件的使用只能通过apply plugin方式不能使用plugin{}的方式。
//config.gradle中
ext {
mVersion = '1.1.0'
}
//build.gradle
apply from: "../config.gradle"
println("mVersion${ext.mVersion}")
5.2 Task
5.2.1 执行动作
-
上文也提到过,gradle的一次构建,就是一系列task的执行,task就是gradle的一个执行单元。
-
一个gradle项目中包含若干个project,每个project中包含若干个task。
-
一个gradle task里面会包含若干个action。
5.2.2 创建task
task一般都定义在plugin中,我们也可以自己定义task,给其配置名字,描述,分组,action,依赖关系等。
//build.gradle
project.tasks.register("aTask") {
println 'in configuration phase'
it.description("this is a sample task")
it.dependsOn("hello")
group("build")//IDE右边的Gradle快捷工具栏中,会看到该task被归类到build分组内,方便查找
description "this is a gradle hello sample" //描述
it.doLast {
println("do action >>1") //向当前task的action链路后追加action
}
it.doFirst {
println("do aciton >>2")//向当前task的action链路前查action
}
}
project.tasks.register("hello") {
it.doLast {
println("hello task is excuted-")
}
}
以上只是一个简单的例子,task的创建使用可以单开一个章节。
6 结
本文主要是在一个可能比较高的视角,谈了谈gradle相关的概念,生命周期特性,插件task等。掌握了这些之后,还有一些更深入更细致的话题值得探索。比如
- 自定义插件,细致的Task详解
- gradle tansformer
- 构建性能如何提升
- AGP常见的配置
- Gradle项目实战
作者:Drummor
链接:https://juejin.cn/post/7204389419700518967
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
作者:Drummor
链接:https://juejin.cn/post/7204389419700518967
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。