视角拉高,系统性地梳理下Gradle
本文作者
作者:Drummor
链接:
https://juejin.cn/post/7204389419700518967
本文由作者授权发布。
1.1 Gradle 、AGP(Android Gradle Plugin)、 buildTools分别是什么,他们之间什么关系?
1.1.1 Gradle
Gradle是基于JVM的构建工具。他本身使用jave写的,gradle的脚本也就是build.gradle通常是用groovy语言。
1.1.2 Android BuildTools
//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
android { buildToolsVersion "33.0.1"}
1.2 gradleWrapper、gradle-user-home、GradleHome
1.2.1 GradleWrapper
/gradle/wrapper/
├── gradle-wrapper.jar
└── gradle-wrapper.properties
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
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.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任务图。
2.3 执行阶段
tasks.register('test') {
println '该语句会在配置阶段执行'
doLast {
println '该语句会在执行阶段运行'
}
}
3.1 监听初始化阶段回调
//settings.gradle配置执行完成
gradle.settingsEvaluated {
...
}
gradle.properties 加载完成。 init脚本执行完成。 Settings对象创建成功,并且从gradle.properties加载的值也注入到了settings中。
// 所有 Project 对象创建(注意:此时 build.gradle 中的配置代码还未执行)
gradle.projectsLoaded {
...
}
3.2 配置阶段的回调
beforeProject{}/project.beforeEvaluated{}:会在每个project配置之前调用。 afterProject{}/after.beforeEvaluate:会在build.gradle脚本"从头跑到尾"之后调用。 当所有的gradle.build都执行完毕后,会调用projectEvaluted{}。 最后,task图构建完成后,回调gradle.taskGraph.whenReady {}和gradle.taskGraph.taskExecutionGraphListener.graphPopulated()。
3.3 hook执行阶段
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
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.1 Gradle Plugin
5.1.1 Apply plugin方式引入
//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配置
}
//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{ 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 其他
ext {
mVersion = '1.1.0'
}
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
//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的创建使用可以单开一个章节。
自定义插件,细致的Task详解 gradle tansformer 构建性能如何提升 AGP常见的配置 Gradle项目实战
最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!
推荐阅读:
扫一扫 关注我的公众号
如果你想要跟大家分享你的文章,欢迎投稿~
┏(^0^)┛明天见!