写给小白的Gradle指南

946 阅读23分钟

1. 前言

作为一个Android程序员,Gradle真是最熟悉的陌生人,每天使用又知之甚少,可能连一知半解都谈不上,Gradle的概念都不能很完整的表达出来,聊几句Gradle,不外乎是:构建工具,基于Groovy语言,使用groovy dsl 编写脚本。真的很难懂哦,今天就扒一扒Gradle的面纱。没有实际操作,api如何使用,大部分是概念解析。

Gradle是非常复杂的,概念,流程,生命周期,api,编写脚本 要理解的内容真的很多。独立插件又是一大波知识点。

从零开始对Gradle没有任何概念的同学,要付出一些精力和时间,不是短时间就能理解掌握的。

2.官网的第一句介绍

去官网 Gradle User Manual

虽然Gradle官网有中文站点,访问速度很快 但是没有中文版本,大家可以利用浏览器的翻译功能,翻译不好的地儿就开两个网页中英文对照看(大佬当我没说)

Gradle is an open-source build automation tool focused on flexibility and performance. Gradle build scripts are written using a Groovy or Kotlin DSL.

Gradle是一个开源构建自动化工具,专注于灵活性和性能。Gradle 构建脚本是使用 Groovy  或 KotlinDSL 编写的。

Gradle用户指南的第一句话,可知

  1. Gradle是一个构建工具

  2. Gradle的构建脚本使用 groovy 或 kotlin DSL编写

    1. Gradle的构建脚本 并不是Gradle本身 使用 groovy 或 kotlin DSL编写

    2. groovy 或 kotlin DSL 想要表达的意思是

      1. groovy DSL
      2. kotlin DSL
    3. 先不说 groovy ,kotlin 作为Android程序员可太熟了, 可kotlin DSL是个啥玩意

能够得出的结论是:

Gradle 并不是一门语言 是构建工具, Gradle使用了groovy 语言 或 kotlin 语言 用来编写Gradle 的构建脚本。

有三个概念

  1. gradle 本身
  2. gradle的脚本
  3. 脚本使用的语言:groovy 或 kotlin

3.Gradle是什么

3.1 构建工具

Gradle并不是一门语言,是构建工具。

Gradle也是一个软件程序,这个程序的作用是帮助程序员构建软件。

嗯,构建其他软件是什么意思??

在Android中,使用gradle构建项目,帮助程序员下载依赖,编译程序员内的资源,生成apk包

web前端使用npm构建项目,同样下载依赖,编码完成后打包

简单的说:构建工具用来下载依赖,项目打包的。使程序员能够专心的写业务代码,避免被项目配置,打包流程这种麻烦繁琐的事情占用过多的精力。

回忆一下最初学Java

使用javac 编译源码 生成class文件,使用命令生成jar包 打JAR包那些事 - 掘金 (juejin.cn)

使用eclipse写SSH,需要手动引入一堆的jar包,项目才能运行。

在开发前,程序员自己下载开发需要的三方jar包。开发后,程序员自己使用命令编译源码,生成可执行文件。

虽然java,Android官方肯定提供了许多工具帮助开发者完成打包过程。但是太麻烦太麻烦了,通过引入构建工具,构建工具把原本的打包过程细分未一个一个步骤,逐步实现。

程序员提供代码,图片,视频等程序资源提供给构建工具,工具还程序员一个能够运行的软件包。

3.2 能够构建任何软件

Gradle作为一个构建工具,它真正优秀和强大的地方在于,它并不是只能服务于某一门语言或平台的工具软件。

它是通用的构建工具,能够构建任何项目,比如:Android,c++,java,kotlin,spring,swift等等。

它为什么这么牛呢?

Gradle抽象一套软件构建的逻辑,它并不承担任何实际工作。Gradle没有面面俱到将所有语言的构建流程都实现一遍,它抽象出三个构建阶段:初始化,配置,执行。

同时提供插件 功能,当使用Gradle构建Android项目,集成Android插件。打包java项目就集成Java插件。

用程序员行话来解释Gradle,它是面向对象的设计思路,使用抽象类抽象出三个方法:init,configure,execute。

为了实现某一门语言的构建工作,继承抽象类,各自按照平台特性实现方法,比如:

抽象类 BasePlugin

子类:AndroidPlugin,javaPlugin,kotlinPlugin等等

所以Android 和 Gradle 是没有任何联系的两个东西,Google官方开发出基于Gradle的构建插件才把Android和Gradle 联系起来,也就是大家熟悉的AGP

AndroidStudio是Google官方的定制idea,天生的就把Gradle和Android结合起来,不需要程序员二次配置,这也是为什么在Android中会决定Gradle和Android紧密的想一个东西的原因

总结:

Gradle是一个构建工具同时又想一个开源框架,抽象出一套通用的构建流程,允许开发者自行实现构建任务,从而达到能够构建任何项目的效果。

4.第一个Gradle项目

作为Android程序员与Gradle打交道,往往是使用AndroidSutdio的时候,难免对Gradle的理解与Android项目联系在一起。

我们看看如何创建一个Gradle项目,增强对Gradle的认识。

4.1 环境配置

安装 Gradle

  1. 需要java环境,Java8以上 ,使用 java -version 检查Java版本

  2. 下载Gradle Gradle | Releases

    1. 有两个下载选项,binary-only 是只包含核心类库的版本 complete 是包含文档,源码的版本
    2. 下载完成后,解压的任意目录,添加bin目录的环境变量即可:D:\xxx\gradle-7.4.2\bin
    3. 配置完成后,使用命令 gradle -v 输出版本号 配置成功

Untitled.png

4.2 创建Gradle项目

创建Gradle项目非常简单 在任意目录下使用命令 gradle init ,命令行程序有步骤提示,引导开发者,如下所示,创建一个基础项目

D:\document\gradle\demo\demo>gradle init

Select type of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4] 1

Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Groovy) [1..2] 1

Generate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no]

Project name (default: demo):

> Task :init
Get more help with your project: Learn more about Gradle by exploring our samples at <https://docs.gradle.org/7.4.2/samples>

BUILD SUCCESSFUL in 11s
2 actionable tasks: 2 executed

生成的目录结构如下所示,是不是很眼熟了,这就是Gradle项目最初的摸样

Untitled2.png

再来构建一个Java项目,也可以构建c++,groovy,kotlin项目

Sample Index (gradle.org)

D:\document\gradle\demo\javaDemo>gradle init

Select type of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4] 2

Select implementation language:
  1: C++
  2: Groovy
  3: Java
  4: Kotlin
  5: Scala
  6: Swift
Enter selection (default: Java) [1..6] 3

Split functionality across multiple subprojects?:
  1: no - only one application project
  2: yes - application and library projects
Enter selection (default: no - only one application project) [1..2]

Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Groovy) [1..2] 1

Generate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no]

Select test framework:
  1: JUnit 4
  2: TestNG
  3: Spock
  4: JUnit Jupiter
Enter selection (default: JUnit Jupiter) [1..4] 1

Project name (default: javaDemo):
Source package (default: javaDemo):

> Task :init
Get more help with your project: <https://docs.gradle.org/7.4.2/samples/sample_building_java_applications.html>

BUILD SUCCESSFUL in 27s
2 actionable tasks: 2 executed

生成目录如下,多了app目录,app目录下是java src 目录和 build.gradle文件。

Untitled3.png

在当前目录下使用命令 ./gradlew run 运行项目,通过命令行可以看到Gradle项目的编译过程,下载依赖最终运行java项目 输出 Hello world!

4.3 项目结构介绍

settings.gradle 文件: 用于确定哪些模块参与构建;

根目录下 build.gradle 文件: 用于定义所有子模块公共的配置参数;

模块下 build.gradle 文件: 用于定义子模块的配置参数,它可以覆盖项目级 build.gradle 文件中定义的配置;

gradle/warpper: 负责自动下载安装项目所需的 Gradle 环境的脚本;

**gradlew :**Linux下Gradle任务执行脚本

**gradlew.bat :**Windows下Gradle任务执行脚本

gradle.properties: 用作项目级 Gradle 配置项,会覆盖全局的配置项;

local.properties: 用作项目的私有属性配置,例如 SDK 安装目录,一般不把 local.properties 加入版本控制。

4.4 小结

经过实际操作,小小的上手一下 是不是对Gradle有更深的理解了。

Gradle作为构建工具的同时,又可以说是一款开源框架,它只定义了编译流程,不限定具体执行什么任务,编译什么语言。

Gradle是单独的一个应用程序,与我们编写的代码是两个东西,只在构建项目时起作用。

5. Gradle—Java项目

之前提到过,Gradle也是一个应用程序,它是纯Java编写的,一个正宗的运行在JVM上的java项目。

???Gradle不是使用groovy语言么,怎么又变成Java编写的了。

Gradle的脚本 settings.gradle , build.gradle 是使用groovy语言,是程序员使用groovy或kotlin 写给Gradle程序看的。

但是Gradle本身是用Java编写的。

打开刚刚下载的Gradle目录 可以看到bin目录,lib目录,如果下载的是包含源码的版本还可以看到src目录,在 gradle-7.4.2\src\bootstrap\org\gradle\launcher 能够找到Gradle的启动类,都是正宗的java代码

public class GradleMain {
    public static void main(String[] args) throws Exception {
        String javaVersion = System.getProperty("java.specification.version");
        if (javaVersion.equals("1.6") || javaVersion.equals("1.7")) {
            String gradleVersion = GradleVersionNumberLoader.loadGradleVersionNumber();
            System.err.printf("%s %s requires Java 1.8 or later to run. You are currently using Java %s.%n", "Gradle", gradleVersion, javaVersion);
            System.exit(1);
        }

        Class<?> mainClass = Class.forName("org.gradle.launcher.bootstrap.ProcessBootstrap");
        Method mainMethod = mainClass.getMethod("run", String.class, String[].class);
        mainMethod.invoke(null, "org.gradle.launcher.Main", args);
    }
}

那么Gradle程序的运行机制是什么呢?

它并不像平常的Java程序,启动一次进程就消失了,下次运行开启的是一个全新的进程。

别的语言程序是Gradle是个啥情况我不太清楚,对于Android来说构建项目是个很频繁的过程,如果每次都新开启一个构建进程,是个很耗时的情况。

第一次构建项目 和 之后构建项目的耗时体验还是差距很大的。

为此Gradle设计了三个进程配合完成构建任务

  1. client 进程
  2. wrapper 进程
  3. Deamon 进程

client进程非常轻量,正如它的名字一样,它是客户端进程每次构建项目的时候都会创建一个新的,作用是发起进程间通信,向Deamon进程发送任务,Deamon进程是负责干活的进程

Deamon进程第一次启动后常驻后台,不依赖 AS 而是独立存在,是一个守护进程一段时间内没有使用会自动关闭(据说是三小时)

Deamon进程会在内存中缓存项目结构,文件,任务等的信息。

通过命令 gradle --status 可以查看已经启动的deamon进程,因为配置gradle是最新的7.4.2,所以只会展示当前版本的deamon进程,

但实际上 使用不同版本的gradle时会启动对应版本的gradle,比如:运行了两个项目,7.0和6.0 那么系统中就会存在 两个deamon进程,这也是Gradle占用内存的一个原因,可以通过设置本地统一gradle版本进行优化

在上一节使用命令 gradle init 创建项目的时候 第一次运行的时候 看到命令行 会有关于Deamon进程启动的信息展示,之后在运行就没有了

wrapper进程唯一的 作用是下载Gradle版本,每个Gradle项目中都有一个Gradle目录。构建时client 进程发现所需版本的 Gradle 本机没有,那么就会启动 wrapper 进程,根据 gradle.properties  里面的参数去 运行 gradle-wrapper.jar 里面的下载程序去下载 Gradle 文件

Untitled4.png

6. 关于DSL

在学习Gradle时 经常会听到 “用 groovy DSL编写 gradle脚本”,kotlin火了之后 又会听到 “用kotlin DSL编写gradle脚本”

groovy我不认识,据说也是运行在Java虚拟机上的语言 和Java无缝衔接,但是从来没用过 不好评论。 DSL 就更别提了 见识短浅的我 如果不是学习gradle 压根就没听过 DSL的概念,特定领域语言

什么东西?

问题已经有了 怎么解决呢? groovy我不认识,dsl 没听过。 可kotlin我认识啊,官方推荐语言每天使用, 可 kotlin dsl 也没听过啊, 从熟悉的东西入手,先了解一下kotlin dsl。

6.1 DSL分析

DSL domain-specific language 领域专用语言,关于DSL的概念就不详细说了,感觉说了也没啥用更容易引起歧义,不好理解。

DSL语言分两种

外部 DSL

从零开始构建的语言,需要实现语法分析器等,不依赖某一门通用的编程语言,比如:HTML、CSS、SQL

内部 DSL

需要依赖某一门通用的编程语言,这个语言被称为宿主语言,比如:kotlin,groovy

先分析下外部DSL

sql ,html ,css,无论哪一端的开发者多多少少应该都用过,它们与java,kotlin,swift这种通用语言相比有什么区别呢? 用查询举例,

sql是只能使用数据库操作的语言,Java同样也可以用文件存取数据。

模拟一个场景,普通文件存储100条数据,数据库存储100条数据,把数据取出输出的控制台。

Java想要实现这个功能需要程序员做很多事情,文件流操作,数据转换,循环遍历等等

而SQL,它天生就是为了数据库存取操作而生的。 使用 select语句 就能所有的数据查询出来。

Java 和 sql 差不多的需求,但是需要开发者实际编写的代码数量差距非常大。

为什么会这样?

SQL 是 DSL, 针对于数据库操作领域专门设计的语言,因为SQL面对的问题少,它只需要考虑几种特殊的场景,所以它可以对这些场景进程高度的封装。

假设 数据的操作就是 CURD,就这四个固定场景,那么设计者就可以尽情的对CURD 进行封装,用起来越简单越好,压根就没有别的使用场景,不用考虑灵活 扩展

Java不同,它是通用语言,要做的事情和能做的事情都非常多,有过设计组件经验的同学应该有体会,灵活意味使用起来会麻烦。

SQL看似简单,但是底层做了非常多的事情,SQL有专门的解释器,其实就是逻辑处理,Java要写的文件流,循环遍历,sql一样少不了,只是SQL针对于单一场景封装优化的更好。

HTML,css也是同理,存在对应的解释器,解释器替开发者做了绝大部分工作,所以才可以一个标签就可以出效果。

外部DSL总结

因为dsl 语言专门只需要考虑特定场景,设计者可以极致的封装,使代码编写简单,达到 一条语句 完成 一大块通用语言 才能实现的效果

DSL的特点是,编码时 代码看起来像 语句 或 关键字构成的,比如 select * from tb_user

6.2 kotlin DSL

外部DSL 是一种全新的语言,内部DSL一门通用语言特性而实现的,再官网找到一篇关于DSL的文,

类型安全的构建器 - Kotlin 语言中文站 (kotlincn.net) 通过使用命名得当的函数作为构建器,结合带有接收者的函数字面值,可以在 Kotlin 中创建类型安全、静态类型的构建器。 类型安全的构建器可以创建基于 Kotlin 的适用于采用半声明方式构建复杂层次数据结构领域专用语言(DSL)

官方描述关键点:

”使用命名得当的函数“

“类型安全、静态类型的构建器”

“适用于采用半声明方式构建复杂层次数据结构”

参考官方写个简单的测试用例

class ExampleUnitTest {
    //代码调用
    @Test
    fun test() {
        val result = html {
            title = "我是title"
            body = "我是body"
        }
        println("结果:${result.title} -- ${result.body}")
    }
}

//定义数据类
class HTML {
    var title: String = ""
    var body: String = ""
}

//定义高阶函数 当作构造方法
fun html(init: HTML.() -> Unit): HTML {
    val html = HTML()
    html.init()
    return html
}

---输出结果
结果:我是title -- 我是body
BUILD SUCCESSFUL in 2s

//调用
html {
 // ……
}

//原理
fun html(init: HTML.() -> Unit): HTML {
    val html = HTML()
    html.init()
    return html
}

是不是有点那意思了,代码非常简单, 看起来像是写了一个 名为html的代码块,实际效果是创建了一个HTML对象

内部原理是kotlin的高阶函数 和 扩展函数,html函数接收了 名为init的 HTML扩展函数,因为扩展函数的特性,作为参数存在的init()函数可以被HTML实例调用

HTML对象的构造过程仍然存在 ,仍然需要new对象,只不过细节被语法特性屏蔽了。

小总结:

kotlin dsl 作为内部dsl,是基于kotlin函数的语言特性实现的

kotlin官方并没有提供如 select,update语句 一样的官方 dsl,只是提供了一种机制 让开发者自行处理,感觉dsl 是另一种封装方式,本质上是方法调用 与 sql,html等 dsl语言相比还是有不小的差距,毕竟目前看来kotlin dsl 的使用方式 就是 关键字+代码块 如:html {}

官方文档的标题 类型安全的构建器 还是很贴切的

6.3 Groovy DSL

看一个非常经典的 Android library 的 build.gradle 脚本,省略部分代码

plugins {
    id 'com.android.library'
}

android {
    compileSdk 32

    defaultConfig {
        minSdk 23
        targetSdk 32
        versionCode 1
        versionName "1.0"
    }
}

dependencies {

    api 'com.squareup.retrofit2:retrofit:2.9.0'

}

上述代码可以分为三大块:

  1. plugins

    1. 对应 org.gradle.plugin.use.PluginDependenciesSpec 是Gradle提供的接口, id 'xxx' 是接口提供的抽象方法
    public interface PluginDependenciesSpec {
        PluginDependencySpec id(String var1);
    }
    
  2. dependencies

    1. 位于 org.gradle.api.Project 接口中,是一个抽象方法,参数是 groovy的 Closure 对象
    2. Closure 可以理解为 Java中的 lambda表达式,函数接口 和 kotlin中的高阶函数,允许方法传递一段代码块
    public interface Project {
    		//...省略 
    		
    		void dependencies(Closure var1);
    	
    		//...省略 
    }	
    
  3. android

    1. 是个扩展属性,Google提供的 com.android.tools.build:gradle:xxx 插件中实现,并不是Gradle原生的东西
    2. 原理是利用Gradle的 org.gradle.api.Project 接口提供的 getExtensions() 方法,设置扩展属性。
    3. gradle扩展 可以理解为 另类的xml配置,作用在于收集数据。xml解析后用JavaBean接收数据,gradle扩展也是同样
    //模拟大概代码如下:
     
    //针对 当前project 创建扩展属性 
    project.getExtensions().create("demoUser", UserExtension.class);
    
    //UserExtension 相当于javaBean 承载数据
    public class UserExtension {
    
        private String name;
        private int age;
    		
        //getter setter省略
    }
    
    //在gradle脚本中使用
    demoUser{
        name = "xixixi"
        age = 12
    }
    
    

上述的Gradle的接口文件,方法定义是Gradle使用Java编写的,Google提供的Android插件早些版本是用Java编写,最近几个版本用kotlin编写

因为Gradle脚本使用 groovy语言,它的语法糖省略了一些在Java 或 kotlin中调用函数的必要内容,build.gradle不省略语法的写法如下:

  1. plugins代码块下的 id实际上是参数为String的接口抽象方法,正常的方法调用 与Java一致,id('com.android.library') 不省略括号
  2. android 代码块 是Google插件提供的功能,实际上是扩展属性的赋值,不省略复制等号
  3. dependencies 代码块,是project接口的抽象方法,参数是groovy的 Closure 对象,相当于lambda表达式,高阶函数。 本质上dependencies 是一次方法调用不省略 参数括号

经过编译是可以运行的

plugins {
    id('com.android.library')
}

android {
    compileSdk = 32

    defaultConfig {
        minSdk = 23
        targetSdk = 32
        versionCode = 1
        versionName = "1.0"
    }

    
}

dependencies ({
    api 'com.squareup.retrofit2:retrofit:2.9.0'
})

小总结:

groovy DSL 比kotlin DSL要更复杂一点,语法规则上有函数的省略,Closure 闭包,官方提供的dsl等等机制,学习起来有一定难度。

如果不熟悉groovy 的语法,没有实际使用过 只看看api介绍 很难对groovy这么语言形成概念。

groovy的语法省略太多,对于熟悉groovy的人来说确实很棒,但是对于新手真是麻了,学习成本太高了。

比如:

plugins代码下的 id com.android.library 与 Android 代码块下的 compileSdk 32 ,对于没有研究过groovy的人来说,谁能想到一个是方法调用,一个是赋值呢。

6.4 DSL参考与总结

Gradle DSL Version 7.4.2

Android Gradle plugin API reference  |  Android Developers (google.cn)

类型安全的构建器 - Kotlin 语言中文站 (kotlincn.net)

分享一个Kotlin 高端玩法:DSL! (qq.com)

干货 | 实现一个属于你的“语言”-携程Kotlin DSL开发与实践 (qq.com)

Kotlin 修炼手册(15)DSL - 掘金 (juejin.cn)

一杆到底:DSL 领域特定语言-阿里云开发者社区 (aliyun.com)

谈谈 DSL 以及 DSL 的应用(以 CocoaPods 为例) - 知乎 (zhihu.com)

从groovy 的 DSL来看,它的学习过程是非常陡峭的。所以大家千万千万 千千万万不要轻易用 kotlin DSL去设计代码,老老实实写函数就好了,大家一眼就能看明白,千万别炫技。如果用了kotlin dsl设计代码 请留下足够丰富的文档,让后来人能看懂。

对于DSL本身,我感觉是一种在特定场景下的高度封装,好处是使用真的简单,坏处就是如果文档不丰富真的难懂。

在如SQL,HTML等 独立的DSL语言中,一个语句,标签或关键字 就能达到明显效果的原因在于有专属的词法分析,解析器等组件在背后工作。

groovy,kotlin等 内部DSL中,一个简洁的语句 或 代码块的背后有 语言特性 或 存在对应的类在背后执行逻辑。这些干活的类可能是官方定义的,也可以是配合 Gradle插件机制 由程序员自定义的。

7. Gradle脚本

Gradle常见的脚本有两个 settings.gradle build.gradle ,还有一个不常见的 init.gradle

Gradle脚本使用groovy语言,groovy 也是一种JVM语言,能够和Java无缝衔接。上述脚本在Gradle运行时 会被编译为class文件,生成对象Java虚拟机中运行。之前也提过Gradle 就是个Java程序

打印一下类信息

熟悉的 getClass(),所有反射的api都能调用。熟悉的堆栈打印方式 new Throwable().printStackTrace() 。 一切都很像Java,但是整体看Gradle脚本就一点都不像Java

//settings.gradle中输出日志
println("setting class -------- ${this.getClass()}")
println("setting Superclass -------- ${this.getClass().getSuperclass()}")
println("gradle class --------  ${gradle.getClass()}")
println("gradle class --------  ${gradle.getClass().getSuperclass()}")

setting class -------- class settings_aj0eixozohtkc94mwkss4h8rf
setting Superclass -------- class org.gradle.initialization.SettingsScript
gradle class --------  class org.gradle.invocation.DefaultGradle_Decorated
gradle class --------  class org.gradle.invocation.DefaultGradle

//build.gradle中输出日志
println("project class --------  ${project.getClass()}")
println("project Superclass--------  ${project.getClass().getSuperclass()}")
project class --------  class org.gradle.api.internal.project.DefaultProject_Decorated
project Superclass--------  class org.gradle.api.internal.project.DefaultProject

//还可以打印堆栈信息 查看调用顺序
new Throwable().printStackTrace()

在Java中编写一个类是有明确语法的,必须声明Class,所有代码写在Class代码块中,代码一板一眼必须按照规则编写,随便写是不行的编译不通过。

脚本是没有硬性的规则,可以直接写代码。脚本执行时,脚本解析器按顺序从第一行逐行执行。

所以Gradle脚本可以看作一个另类的Java文件

  1. 默认导入很多类,不需要导包
  2. 默认实现了某些接口 或 抽象类的文件,可以直接调用 接口暴露的方法
  3. 由于是脚本文件,可以直接写代码,定义变量,方法,没有绝对语法编写规则
  4. groovy的语言特性,有很多简写的语法糖

比如:

settings.gradle 默认实现好了 org.gradle.api.initialization.Settings 接口,可以直接调用接口定义的方法。最常用的 include ':app' ,实际上就是个方法调用 void include(String... var1);

但由于语法特性 方法调用和Java看起来又完全不同。

settings.gradle 如果按照Java的写法可能会是。 但用于Gradle脚本的特性,一个空脚本就已经默认继承类了,可以调用方法,随意编码。

public class DemoSetting implements Setting{
    
    public DemoSetting() {
        include("app");
    }

    @Override
    public void include(String name) {
       // 一堆实现代码
    }
}

小总结:

  1. gradle脚本理解成一个有很多语法省略的特殊Java类
  2. 代码没有严格编写规则,可以随意定义
  3. 每个脚本已经默认继承了接口或抽象类,把它当成一个Java类吧 ,大胆的调用接口暴露方法
  4. 难点在于不知道接口暴露了什么方法,有啥参数,emm 决解办法就是下载个源码看看

init.gradle 对应 org.gradle.api.invocation.Gradle接口

settings.gradle 对应 org.gradle.api.initialization.Settings 接口

build.gradle 对应 org.gradle.api.Project 接口

原理可看视频:你的 Gradle脚本究竟是什么?_哔哩哔哩_bilibili

7.1 init.gradle

没有了解过Gradle的同学应该不知道这个东西,它确实也没啥用,是更大范围的初始化文件。

settings.gradle 和 项目根目录下的 build.gradle 是针对当前项目的通用配置。

init.gradle是针对所有使用了xxx版本Gradle项目的通用配置。

如下截图是当前电脑内,AndroidStudio下载过的所有Gradle版本,每个目录下都有init.d目录,用来存放init.gradle 默认是空的,没有配置文件。如果设置了配置文件,那么当依赖此版本的项目开始构建,就会默认先执行。

由于init.gradle 的范围太大了,一旦配置所有项目都受影响,几乎是用不到。

Untitled5.png

8. Gradle构建生命周期

Gradle构建分为三个过程:初始化,配置,执行

8.1 初始化阶段

执行 init.gradle 脚本,生成 Gradle对象

执行 setting.gradle 脚本,生成 Setting对象,执行include函数,确定有哪些项目会参与构建,每个项目会对应生成一个Project对象,最终形成项目结构

8.2 配置阶段

首先执行根目录的build.gradle 再执行每个项目的 build.gradle 脚本,执行脚本的过程中 没那么神秘 就是 一次执行代码而已。

但代码执行后会有些额外的产物,有依赖关系的任务,任务间彼此依赖,形成一个有向无环图。

这是Gradle的核心,其实Gradle更像一个任务框架。

想要实现Java打包,Android打包时一个复杂,且有一定顺序,规律的巨大任务。大任务又可以解析为一个个小任务。任务之间具有规则和依赖关系。

具体的打包步骤 是实际情况而定,Java打包 和 Android打包肯定不同。 Gradle出色的地方在于,它把打包抽象为一个流程,并提供插件机制,插件如何实现Gradle不管。Java,Android 或是 c++,具体的打包操作自己实现。

插件需要什么配置信息,通过build.gradle 配置。插件读取到配置信息,生成一个个任务,保存在Gradle中的TaskContainer

配置过程的最终成果就是生成:当前项目的任务有向无环图

Untitled6.png

8.3 执行阶段

执行阶段 好像没啥可说的,执行配置阶段生成的任务

配置文件中有哪些常用api ,生命周期回调 可看这两篇:

深度探索 Gradle 自动化构建技术(三、Gradle 核心解密) - 掘金 (juejin.cn)

Gradle 爬坑指南 -- Gradle 核心模型、Hook 函数、ext 扩展属性、Project API - 掘金 (juejin.cn)

9. 总结—偷图

推荐视频

来自Gradle开发团队的Gradle入门教程_哔哩哔哩_bilibili

你的 Gradle脚本究竟是什么?_哔哩哔哩_bilibili

系列文章

补齐Android技能树「Gradle篇」 - coder_pig的专栏 - 掘金 (juejin.cn)

Gradle 爬坑指南 -- 导论 - 掘金 (juejin.cn) 这篇总结了N个资源 ,作者本身也有写解读

深度探索 Gradle 自动化构建技术(一、Gradle 核心配置篇) - 掘金 (juejin.cn)

看完这一系列,彻底搞懂 Gradle - 知乎 (zhihu.com)

为什么说 Gradle 是 Android 进阶绕不去的坎 —— Gradle 系列(1) - 掘金 (juejin.cn)

Untitled8.png

14aa4ce3fb434e37b095678cc1b18630_tplv-k3u1fbpfcp-zoom-in-crop-mark_3024_0_0_0.webp Untitled7.png