首页 > 其他分享 >关于 Gradle 你应该知道的知识点

关于 Gradle 你应该知道的知识点

时间:2023-06-22 14:03:32浏览次数:31  
标签:知识点 插件 script gradle 构建 关于 Gradle build


前言

对于Android开发者来说,Gradle也可以说是熟悉的陌生人了,可以说天天会用到Gradle,但对于Gradle的一些原理与细节又往往不太了解
本文主要介绍Gradle的一些基础知识与原理,如果对你有所帮助,欢迎点赞

本文主要包括以下内容:

  1. Gradle到底是什么?
  2. Gradle Wrapper是什么?
  3. AGP到底是什么?
  4. gradle.properties是什么?
  5. settings.gradle是什么?
  6. build.gradle是什么?
  7. Gradle生命周期是怎样的?

1. Gradle到底是什么?

Gradle也用了这么久了,如果要用一句话来描述Gradle,该如何回答呢?一个依赖管理框架?一个构建框架?

Gradle 是一个 运行在 JVM 的通用构建工具,其核心模型是一个由 Task 组成的有向无环图(Directed Acyclic Graphs).

关于 Gradle 你应该知道的知识点_android studio

2. Gradle Wrapper是什么?

说起来我们一直在使用Gradle,但仔细想想我们在项目中其实没有用gradle命令,而一般是使用gradlew命令,同时如下图所示,找遍整个项目,与gradle有关的就这两个文件夹,却只发现gradle-wrapper.jar

关于 Gradle 你应该知道的知识点_android studio_02

那么问题来了,gradlew是什么,gradle-wrapper.jar又是什么?

wrapper的意思:包装。
那么可想而已,这是gradle的包装。其实是这样的,因为gradle处于快速迭代阶段,经常发布新版本,如果我们的项目直接去引用,那么更改版本等会变得无比麻烦。而且每个项目又有可能用不一样的gradle版本,这样去手动配置每一个项目对应的gradle版本就会变得麻烦,gradle的引入本来就是想让大家构建项目变得轻松,如果这样的话,岂不是又增加了新的麻烦?
所以android想到了包装,引入gradle-wrapper,通过读取配置文件中gradle的版本,为每个项目自动的下载和配置gradle,就是这么简单。我们便不用关心如何去下载gradle,如何配置到项目中。

至于gradlew也是一样的道理,它共有两个文件,gradlew是在linux,mac下使用的,gradlew.bat是在window下使用的,提供在命令行下执行gradle命令的功能
至于为什么不直接执行Gradle,而是执行Gradlew命令呢?
因为就像wrapper本身的意义,gradle命令行也是善变的,所以wrapper对命令行也进行了一层封装,使用同一的gradlew命令,wrapper会自动去执行具体版本对应的gradle命令。
同时如果我们配置了全局的gradle命令,在项目中如果也用gradle容易造成混淆,而gradlew明确就是项目中指定的gradle版本,更加清晰与明确

3. AGP到底是什么?

AGPAndroid Gradle Plugin,即android官方开发的Gradle插件,在了解AGP之前,我们先介绍一下什么是插件?

Gradle 本身是一个通用的构建系统, 它并不知道你要编译的是 Java 还是 C. 如果是在Java 中需要调用 javac.java 文件编译为 .class 文件, 而 C 则需要调用 gcc.c 文件编译为 .o 文件. 那么这些构建流程如果让每个开发者自己去管理就太麻烦了. 所谓插件, 就是将某种类型的编译的模板.

AGP也就是是一系列适合Android开发的Gradle插件的集合,比如com.android.application
AGP 插件提供了compileKotlin,compileJava,processResource等一系列Task, 并设置了Task之间的依赖关系. 同时还提供了很多可配置属性. 而使用者只需要在 build script 中通过 plugins {...} 引入插件, 根据项目情况配置几个属性, 即可实现自定义的 Android 构建. 通过AGP插件可以快速实现Android项目的构建,这就是AGP插件的意义,其执行过程中的task列表如下所示

关于 Gradle 你应该知道的知识点_生命周期_03

4. gradle.properties是什么?

除了GradlewAGP,我们也经常会用到gradle.properties,我们经常在gradle.peoperties中定义一些统一的版本号,如minSdkVersion,targetSdkVersion等,然后再在各个module中通过rootProject.minSdkVersion获取以实现复用

那么问题来了,rootProject是如何获取gradle.properties中定义的值的呢?
答案其实很简单,Gradle 启动时会默认读取gradle.properties, 并加载其中的参数。这跟我们在运行Gradle的时候通过命令行向其传递参数,效果是一样的
当然不同的方式有不同的优先级,指定参数的优先级: 命令行参数 > GRADLE_USER_HOME gradle.properties 文件 > 项目根目录 gradle.properties 文件.

Gradle 使用的两个目录:
Gradle 在执行过程中会涉及到两个目录, 一个是 Gradle User Home 另一个是 Project Root Directory.
Gradle User HomeUser Home 中主要保存全局配置, 全局初始化脚本以及依赖的缓存和日志等文件. 如果开启 build cache 的话, 构建缓存也会存在这里共所有项目共享.
默认为: $USER_HOME/.gradle.
Project Root DirectoryProject 目录则存储与当前项目构建相关的内容. 例如用于增量编译缓存.

总得来说,gradle.properties其实就是一个参数的配置文件,与在命令行传递参数是一样的效果,因此在Project中可以读取到

5. settings.gradle是什么?

当我们在某个目录执行gradle命令时, 约定的会从当前目录查找以下两个文件:

  1. settings.gradle(.kts)
  2. build.gradle(.kts)

我们常常会在settings.gradle中配置module,那么settings.gradle究竟是什么?起什么作用?
所有需要被构建的模块都需要在settings.gradle 中注册, 因此它的作用是描述 “当前构建所参与的模块”.

settings.gradle查找顺序为: 从前目录开始, 如果找到settings.gradle(.kts)则停止, 否则向父目录递归查找.
setting script承担了统筹所有模块的重任, 因此api主要是在操作所参与构建的模块以及管理构建过程需要的插件.
可以通过如下方式注册需要参与构建的模块,项目名称中 : 代表项目的分隔符, 类似路径中的 /. 如果以 : 开头则表示相对于 root project

include(":app", ":libs:someLibrary")

include(":anotherLibrary")
project(":anotherLibrary").projectDir = File(rootDir, "../another-library")

6. build.gradle是什么?

到了我们最熟悉也是最常用的build.gradle了,每个模块都会有一个build.gradle来配置当前模块的构建信息, 根目录模块的build.gradle叫做 root build script, 其他子模块的 build script 叫做 module build script.

项目构建的流程大致如下所示,其中的init script$GRADLE_USER_HOME目录下的init.gradle文件,主要做一些初始化配置
单模块构建的执行流程大致为: init script -> setting script -> build script 而多模块的构建流程, 比单模块多了一步: init script -> setting script -> root build script -> build script

一般而言, root build script 并不是一个实际的模块, 而是用于对子模块进行统一的配置, 所以 root build script 一般不会有太多的内容.

GradleInitialization 阶段还没有执行 build.gradle(.kts) 文件, 真正解析 build script 是在 Configuration 阶段. 但是 build script 的执行比较特殊, 它并不是简单执行所有代码, 其本质是 用代码描述和配置构建规则, 然后按规则执行任务. Build script 作为整个 Gradle 中配置最复杂的脚本, 实际上仅仅做了两件事: 一个是引入插件, 另一个是配置属性

所谓引入插件如下所示,plugins 闭包中还可以通过 version 指定插件的版本, 以及 apply 来决定是否立刻应用插件:

plugins {
    id("com.android.application")
    id("com.dorongold.task-tree")   version "1.4"
    id("com.dorongold.task-tree")   version "1.4"   apply false
}

而所谓配置属性, 实际上是对引入的插件进行配置. 原本 build script 中并没有 android {...} 这个 dsl 属性, 这是 plugin 提供的. 一旦应用了某个插件, 就可以使用插件提供的 dsl 对其进行配置, 从而影响该模块的构建过程. 换个角度看, 这些插件提供的属性配置 dsl 就相当于插件 init 函数的参数, 最终传入到插件中. 当构建执行的时候就会根据配置对当前模块进行编译.

plugins {
    id("com.android.application")
}

android {
    compileSdkVersion(28)

    defaultConfig {
        ....
    }
}
....

7. Gradle生命周期是怎样的?

在了解了上面这些知识后,我们可以开始了解一下Gradle的生命周期。在了解了Gradle的生命周期后,我们可以对Gradle执行的总体流程有一个了解,也可以利用这些生命周期做一些Hook的操作

不同于传统脚本的自上而下执行, 一次 Gradle 构建涉及到多个文件, 主体流程如下:

关于 Gradle 你应该知道的知识点_gradle_04

总体来说, Gradle 的执行分为三大阶段: Initialization -> Configuration -> Execution. 每个阶段都有自己的职责.

7.1 Initialization阶段

Initialization 阶段主要目的是初始化构建, 它又分为两个子过程, 一个是执行 Init Script, 另一个是执行 Setting Script.
Init script 会读取全局脚本, 主要作用是初始化一些全局通用的属性, 例如获取 Gradle User Home 目录, Gradle version
Setting Script就是我们上面提到的settings.gradle

7.2 Configuration阶段

当构建完成 Initialization 阶段后, 将进入 Configuration 阶段. 这个阶段开始加载项目中所有模块的 Build Script. 所谓 “加载” 就是执行 build.gradle(.kts) 中的语句, 根据脚本代码创建对应的 task, 最终根据所有 task 生成对应的依赖图. 我们上面说过"Gradle核心模型是一个 Task 组成的有向无环图(Directed Acyclic Graphs)" 吗? 这个任务依赖图就是在这个阶段生成的.

需要注意的是,Configuration阶段各个模块的加载顺序是无序的,跟依赖关系与加入顺序都没有关系

7.3 Execution阶段

当完成任务依赖图后, Gradle 就做好了一切准备, 然后进入 Execution 阶段. 这个阶段才真正进行编译和打包动作. 对于 Java 而言是调用 javac 编译源码, 然后打包成 jar. 对于 Android 而言则更加复杂些. 这些差异来源于我们应用的插件. 总得来说,就是开始执行task

7.4 生命周期 Hook

Gradle 提供了丰富的生命周期 Hook,我们可以根据我们的需要添加各种HooK

关于 Gradle 你应该知道的知识点_android_05

根据图中生命周期的位置, 可以清楚地了解到 “生命周期的最晚注册时机”. 比如, settingsEvaluated 是在 setting scriptevaluated 完毕后回调, 那么在 init scriptsetting script 中注册都是没问题的. 但是如果注册在 build script 中, 则无法发挥作用.

同时关于生命周期Hook,还有下面几点需要注意

  1. projectsLoaded之前Project还没有创建,因此只能使用gradlesettings 对象
  2. projectsLoaded 回调时已经根据 setting script 创建了各个模块的 Project 对象, 我们可以引用 project 对象从而设置一些 hook,便是build script还没有被配置,因此拿不到配置信息
  3. 每当一个 build.gradle(.kts) 被执行完毕, 都会产生 afterEvaluate 回调, 代表着 projectevaluate 完成. 从此, project 对象内容完整了, 即: 当前 build.gradle(.kts) 中所有的配置项都能够被访问.
  4. 所有的Project配置结束,会回调projectsEvaluated
  5. Gradle 的核心逻辑就是根据 task 的依赖关系生成有向无环图, 然后依次执行图中的 tasktask graph生成后会回调graphPopulated
  6. 当所有task都执行完毕, 整个构建也宣告结束,这个时候会回调buildFinished

总结

本文主要本文主要介绍Gradle的一些基础知识与原理,包括Gradle各个文件的作用,以及生命周期,构建总体流程,以及生命周期Hook方法等
了解Gradle的这些基础原理,可以帮助我们更好的了解Android构建打包的过程,也方便我们利用Gradle生命周期做一些Hook工作,提升开发效率。
如果本文对你有所帮助,欢迎点赞,收藏,评论~

总结

首先是感觉自己的基础还是不够吧,大厂好像都喜欢问这些底层原理。

另外一部分原因在于资料也还没有看完,一面时凭借那份资料考前突击恶补个几天居然也能轻松应对(在这里还是要感谢那份资料,真的牛),于是自我感觉良好,资料就没有怎么深究下去了。

之前的准备只涉及了Java、Android、计网、数据结构与算法这些方面,面对面试官对其他基础课程的考察显得捉襟见肘。

下一步还是要查漏补缺,进行针对性复习。

标签:知识点,插件,script,gradle,构建,关于,Gradle,build
From: https://blog.51cto.com/u_16163453/6534785

相关文章

  • 关于技术社区中的 Caveat 用法
    我们在逛StackOverflow社区时,除了学习技术之外,也可以学习老外们专业的英语,比如我今天学到一个新的单词:caveat(警告,附加说明)以前表达这个意思,我都是用note,显然Caveat要更加专业一些。在技术社区中,Caveat(警示)是指在使用某项技术、工具或方法时需要注意的注意事项、限制条件......
  • 关于使用rsync命令小技巧-交互式输入密码后-再置于后台运行的方法
    在linux系统中,rsync命令常用于在两台linux主机之间同步数据rsync关于认证可以同ssh的用户密码认证一样,我们执行rsync命令后,会要求输入认证的密码,但很多时候数据特别大时,我们是希望执行过程放在后台运行笔者尝试,在执行rsync命令,如 rsync-az/aa/bb/cc/--exclude"dir01/"--ex......
  • 关于 Spartacus 服务器端渲染的 404 Not found 页面处理
    当启动Spartacus时,路由由Router逻辑处理。将评估四种不同类型的路由:路由应由自定义路由路径处理;客户添加了硬编码路由,并且我们应该优先考虑这些路由。路由是PLP(产品列表页)或PDP(产品详情页)。路由是CMS(内容管理系统)内容页面。路由是未知的(404页面未找到)。当向Sparta......
  • 关于 Java 和 node affinity 这个概念的联系
    在Java开发领域,术语"nodeaffinity"通常不是与Java语言本身直接相关的概念。然而,在某些特定的上下文中,可以将"nodeaffinity"应用于Java应用程序的部署和调度方面。在Java开发中,"nodeaffinity"可能指的是以下几个方面:服务器亲和性:在分布式环境中部署Java应用程序时,可以考虑将......
  • Gradle简介
    gradle跟ant/maven一样,是一种依赖管理/自动化构建工具。但是跟ant/maven不一样,它并没有使用xml语言,而是采用了Groovy语言,这使得它更加简洁、灵活,更加强大的是,gradle完全兼容maven和ivy。基本配置build.gradle首先明确gradle跟maven一样,也有一个配置文件,maven里面是叫pom.xml,而在gra......
  • Gradle 版本管理工具
    Gradle笔记一、简介Gradle是一款Google推出的基于JVM、通用灵活的项目构建工具,支持Maven,JCenter多种第三方仓库;支持传递性依赖管理、废弃了繁杂的xml文件,转而使用简洁的、支持多种语言的build脚本文件。二、安装下载Gradle官网|Installation,选择完整下载......
  • 关于 PyQt5 表格设置数据不显示数据的问题
    在插入一行数据时,需要事先对表格设置表格显示的总数:self.tableWidget.setRowCount(10)之后,再循环插入数据:defafter_list_devices(self,data):row=0self.tableWidget.setRowCount(10)foritemindata['devices']:self.setTableItem(row,0,item[......
  • Android Themes关于totalBar总结
     ustakealookatdefaultthemessuppliedwithAndroidSDK.Belowthemeswillcontrolthebackground,statusbarandtitlebar.Statusbar:VisibleStatusbar:VisibleStatusbar:InvisibleTitlebar:VisibleTitlebar:InvisibleTitlebar:InvisibleThemeThem......
  • 关于NotePad++打开json文件并以树形方式展示
    NotePad++打开json文件并以树形方式展示为了更好查看和分析数据,需要将json数据展开去洞察数据规律,通常如果仅仅简单使用文本工具打开json文件,并不能很友好地观察出数据内容和规律,更不要说比较复杂的多层级的字典内容,因此需要以树形结构更好的观察json数据。为了更加清晰地说明问......
  • 关于vite创建vue3项目@代替src失效的问题
    用vite创建的vue3项目,用@来代替src不生效。报错:[vite]Internalservererror:Failedtoresolveimport"@/views/xxxxxxxxxxxxx"from"src\views\dashboard\index.vue".Doesthefileexist?解决的方法是:先安装pathnpminstall--save-dev@type/node在vite.co......