Gradle 脚本相关知识

Gradle 脚本相关知识

Android 开发过程中,总是要接触到 Gradle,而 Gradle 是基于 Groovy 的一个构建工具。此篇内容记录下这个脚本语言了解和学习过程。

在之前的开发中,编写了诸多的 cmd.sh 脚本。但这种脚本基本依赖于系统。只能运行于 windows 或者 Linux 上。编写这类脚本可以提高开发和维护效率,而学习 Groovy 脚本,显然对自动打包、自动上传等等同样有好处

对于 Android ,Gradle 和 Groovy 是一个绕不开的坎。了解里面的知识点,对引入第三方的 build.gradle 做了什么,也能知道脚本做了什么

视频教程来源于 bilibili ,本篇内容为笔记记录

Gradle wrapper

gradle-wrapper.jar 实际上是一个非常小的。只包含下载功能的jar ,当运行它时,会去下载真正的 gradle.jar 工具。默认路径一般是 用户目录/.gradle

好处是可以托管到 git 项目中,在各个项目同步时,可以保持 gradle 版本一致。在保持版本一致的同时,又不需要托管很大的 gradle 工具

Gradle 进程

如果未经设置。默认调用 Gradle 命令,它会将命令发送给 daemon 进程。这个 daemon 进程会执行输入的命令。

可以设置,每次调用不使用 daemon 。而是单独启动一个进程执行命令

为什么会有这个进程, deamon 是为了加快执行速度,java 的 jvm 冷启动开销很大。所以为了加快构建速度,引入了 daemon

但 daemon 在开发过程中也会出现一些构建问题。一般情况下可以使用,如果出现构建或者文件缓存出现问题等情况。要在任务管理器中结束 Daemon 重新执行

Groovy 语言

这是一种基于 java jvm 的一种动态语言,实际上它在运行的时候,也是生成了字节码运行的。只是通过一些语法糖或者某些方式实现了 java 不具备的功能。

它与 java 是兼容的。在里面可以方便的使用 java 编写脚本,可以使用 java 编写,并不意味着这门动态语言容易, Groovy 语言定义的语法糖以及众多特性需要记忆和了解里面的机制

Groovy 中的 高阶函数lamba 与 kotlin 有相似之处,如果会 Kotln 语法,学习 Groovy 语法会有很多地方感觉似曾相识

Gradle 基于 Groovy ,本质上仅仅是封装了众多的闭包以及高阶函数

Gradle 生命阶段

第一个阶段:初始化阶段,决定项目或 Module 参与构建。创建 project instans

第二阶段: 配置阶段,一般执行项目根目录的 build.gradle。生成项目的 task,用于执行

第三阶段:执行阶段,执行配置阶段生成的项,也就是 task。例如:assemblebuild

task 自定义与使用

task("helloworld"){
    println("hello world")
}

这里自定义了一个 helloworld task,方法执行一段打印。语法与 kotln 类似

运行方式:gradlew helloworld

需要注意的是,task 传入的闭包会在 config 阶段直接执行

task 可以动态创建,例如在 build.gradle 中使用 for 循环创建多个 task。

task 中可以依赖和调用另一个 task:

task ('a'){
    println("task a")
}

task("helloworld"){
    // 这种方式会在打印 hello world 之前,先执行 task a
    dependsOn('a')
    println("hello world")
}

task 也也有 doFirst doLast ,可以在一个任务执行前或者执行后调用另一个方法。例如:

task("helloworld"){
    // 这种方式会在打印 hello world 之前,先执行 task a
    dependsOn('a')
    println("hello world")
}.doFirst { println("first print") }
        .doLast {println("last print")}

顺序是:

> Configure project :app
task a
hello world

> Task :app:helloworld
first print
last print

Configure 阶段会执行 task 内的代码。随后 Task 执行阶段才会执行 doFirst 和 doLast 内的闭包

task 方法绑定在 Project 上。project 提供了相关方法

project.parent.childProjects

afterEvaluate 钩子方法

afterEvaluate 方法,这是一个钩子函数。它会将传入的闭包存放在list 中,暂不执行,会在 config 阶段完成以后执行。也就是 Configure 最后执行

自定义 Plugin

build.gradle 中可以编写代码,实现自定义功能,其基本语法与 kotlin 相似

class MyAwesomePlugin implements Plugin {
    @Override
    void apply(Object target) {
        println("target:" + target)
    }
}

// 下面这两种调用方法是一致的
apply plugin:MyAwesomePlugin
apply(["plugin": MyAwesomePlugin])

不用实例化这个 ckass 。而是直接使用 apply 。

当执行 build.gradle 时,便会调用 void apply() 方法,调用顺序及日志:

> Configure project :app
task a
hello world
target:project ':app'

apply 后面支持 http/s 格式的 url,例如: apply plugin:"https://example.com/script"

当 config 阶段时,会直接 apply 内的方法,那么插件如何在执行阶段执行自定义的脚本呢? 下面是使用 java 语法执行一些自动化工作的示例:

    @Override
    void apply(Project target) {
        println("target:" + target)
        target.getTasks().create("pluginTask").doLast(){
            println("plugin task do last")
        }
        target.getTasks().findByName("pluginTask").doFirst(new Action<Task>() {
            @Override
            void execute(Task task) {
                println("plugin task do first")
            }
        })
        // 和在 build.gradle 中一样的钩子函数
        target.afterEvaluate {}
//        target.evaluationDependsOn()
    }

Dependencies 生效范围

dependencies 中的 compile 依赖一个库,生效范围是对 java 源码进行编译。也就是 compile 是给正在编码的代码进行了依赖。

而 build.gradle 执行阶段,使用 compile 是不会生效以及依赖的。要想在构建中使用。需要在构建脚本的额外的引入依赖:

buildscript{}

buildscript 块中的 compile 将指令换成 classpath 即可。

buildscript 中,可以指定 repositoriesdependencies 。示例:

buildscript{
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath group: 'org.apache.commons', name: 'commons-lang3', version: '1.0'
    }
}

值得注意的是, buildscript 必须放在所有的 build 脚本签名

通过 buildscript 依赖的库,可以在 gradle 脚本中直接使用,就和编写普通的 java 代码一样

buildSrc 约定

如果项目根目录存在一个 buildSrc 目录,则 gradle 会查找这个目录,并执行里面的 java 代码。(使用上边 class MyAwesomePlugin 的代码即可)

目录: buildSrc/src/main/java/Main.java

在里面的 java 代码是可以编译成一个插件并发布的

需要 build.gradle 文件:

//use java library
apply plugin: 'java-library'
//use groovy library
//apply plugin: 'groovy'
sourceSets {
    main{
//      groovy{
//            srcDir 'src/main/groovy'
//        }
        java{
            srcDir 'src/main/java'
        }
        resources {
            srcDir 'src/main/resources'
        }
    }
}

apply 怎么运作的

当 apply 一个 plugin 时的查找阶段,以一个android项目为例:

apply plugin: 'com.android.application'

将会查找根项目的 buildscript 块中的 classpath ,它会查找到:

classpath 'com.android.tools.build:gradle:3.2.1'

而在这个 build:gradle 的 jar 包中会有一个 META-INF/gradle-plugins 目录。然后会查找到这个 jar 包的:

/META-INF/gradle-plugins/com.android.application.properties 文件

这个文件会指定要加载的类的路径,然后再根据这个路径加载这个插件类:

load-path


很推荐看一下这个 视频及教程。对了解 build.gradle 到底怎么运作的有很大帮助

Groovy 官方文档

在 Android 中的相关用法(实际应用)

Copyright: 采用 知识共享署名4.0 国际许可协议进行许可

Links: https://zwc365.com/2020/12/11/gradle-script-about

Buy me a cup of coffee ☕.