Gradle入门

106 阅读5分钟

DSL介绍:全称domain specific language 领域特定语言

核心思想:求专不求全,解决特定问题

APK构建流程

  1. 编译资源+编译代码
  2. 压缩包
  3. 签名
  4. 对齐

一、groovy语法

Gradle注释:

//单行住宿

/*
  多行注释
*/

Gradle支持的类型:

byte、short、int、long、float、double、char、String

int a = 10
int b = 20

println("a = $a")
println("b = $b")

//字符串
String str = "Hello, World"
String str2 = str.toUpperCase()
println("str is $str2")

//列表
def array = [1, 2, 3, 4]
array.add(5)
println("array is $array")

array.forEach {
    println("each item is $it")
}

//映射表
def map = ["name": "pujh", "age": "25"]
println("age is ${map["age"]}")


//循环
for (int i = 0; i < 10; i++) {
    if (i > 5) {
        println("i is $i")
    }
}

//函数
def hello(String name){
    println("hello $name")
}
hello("pujh")

groovy闭包

  • 开放匿名的代码块,可以接受参数,具有返回值,也可以被分配给变量
  • 定义规则:{[params->] staements}
//无参闭包
def c = { println("Hello Closure") }
c()

//有一个参数的时候,参数可以忽略,默认名称为it
def c2 = { println("c2: it = $it") }
c2("idste")

//只有一个参数的时候,名称可以任意指定,但是it就不能用了
def c3 = { name -> println("c2: it = $name") }
c3("idste")
def myAndroid = {
    compileSdkVersion 27
    defaultConfig {
        versionName "1.0"
    }
}

Android a = new Android()
//将闭包与具体对象关联起来
myAndroid.delegate = a
myAndroid.call()

println("myAndroid = $a")

class DefaultConfig {
    private String versionName

    def versionName(String versionName) {
        this.versionName = versionName
    }

    @Override
    String toString() {
        return "DefaultConfig{ versionName = $versionName }"
    }
}

class Android {
    private int compileSdkVersion;
    private DefaultConfig defaultConfig;

    Android() {
        defaultConfig = new DefaultConfig()
    }

    def compileSdkVersion(int compileSdkVersion) {
        this.compileSdkVersion = compileSdkVersion;
    }

    def defaultConfig(Closure closure) {
        //将闭包与具体对象关联起来
        closure.setDelegate(defaultConfig)
        closure.call()
    }

    @Override
    String toString() {
        return "Android{ compileSdkVersion = $compileSdkVersion, defaultConfig = $defaultConfig }"
    }
}

二、Gradle基础

1、gradle命令行

gradle命令行的执行格式:

./gradlew [task-name...][-option-name]

执行任务:

./gradlew clean -q

查看所有子工程:

./gradlew projects

查看所有任务:

./gradlew tasks

Gradle升级:

./gradlew wrapper --gradle-version 8.3

2、gradle文件结构

  • settings.gradle
  • build.gradle
  • gradle.properties

3、gradle生命周期

Gradle构建的3个阶段

  • 1、初始化阶段:解析整个工程中所有的Project,构建所有的Project对应的project对象
  • 2、定义配置阶段:解析所有project对象中的task,构建好所有task的拓扑图
  • 3、执行阶段:执行具体的task及其依赖task

image.png

在2、3阶段中间执行的回调代码afterEvaluate

  • settings.gradle.kts
println("[lifecycle] 我是settings.gradle, rootProject路径是:${rootProject.projectDir}")

//添加 构建生命周期 监听
gradle.addBuildListener(object : BuildAdapter() {

    override fun settingsEvaluated(settings: Settings) {
        println("[lifecycle] 初始化阶段完成")
    }

    override fun projectsEvaluated(gradle: Gradle) {
        println("[lifecycle] 配置阶段完成")
        gradle.rootProject.childProjects.forEach { (name, project) ->
            println("[子工程] $name, 路径是:${project.projectDir}")
        }
    }

    override fun buildFinished(result: BuildResult) {
        println("[lifecycle] 构建结束")
    }
})
  • build.gradle.kts
println("[lifecycle] 我是根目录的build.gradle")
  • app/build.gradle.kts
println("[lifecycle] 我是app的build.gradle")

task("testTask") {
    doLast {
        println("我是 testTask任务")
    }
}

执行结果: .\gradlew testTask -q

[lifecycle] 我是settings.gradle, rootProject路径是:C:\Users\iDste-PC\Desktop\Gradle  
[lifecycle] 初始化阶段完成
[lifecycle] 我是根目录build.gradle
[lifecycle] 我是app build.gradle
[lifecycle] 配置阶段完成
[子工程] app, 路径是:C:\Users\iDste-PC\Desktop\Gradle\app
我是 testTask任务
[lifecycle] 构建结束

几个主要角色

  • 初始化阶段:root project

root project是一个ProjectDescriptor接口,可以通过接口获取根工程的名称、目录

  • 配置阶段:project

在配置阶段就可以拿到project对象

  • 执行阶段:task

三、gradle任务

task("testTask") {
    doLast {
        println("我是 testTask任务")
    }
}

task("test2") {
    dependsOn.add("testTask")
    doLast {
        println("我是 test2 任务")
    }
}

实现参数配置

  • 定义Extension
  • 注册Extension
  • 使用Extension
  • 获取Extension

查看模块依赖项

想要查看整个项目的依赖传递关系,使用命令

gradlew app:dependencies --configuration releaseRuntimeClasspath

x.x.x() 该依赖已经有了,将不再重复依赖 x.x.x->x.x.x 该依赖的版本被箭头所指的版本代替 x.x.x->x.x.x() 该依赖的版本被箭头所指的版本代替,并且该依赖已经有了,将不再重复依赖

configurations {
//    all {
//        resolutionStrategy {
//            force("androidx.appcompat:appcompat:1.1.0")
//        }
//        exclude()
//    }

    create("myConfiguration") {
        println("PUJH myConfiguration")
    }
}

val copyTask = task("copyTask", Copy::class) {
    from(configurations)
    into("allLibs")
}

dependencies {
    val test = configurations.getByName("myConfiguration")
    test(libs.androidx.core.ktx)

//    implementation(libs.androidx.core.ktx) {
//        isTransitive = false //返回true包括传递的依赖,返回false排除传递的依赖
//        exclude(group = "", module = "")
//        force = true
//    }
}

Gradle插件

  • 提供具体的构建功能(Task)
  • 提高代码的可复用性

1、build script脚本

把插件写在build.gradle文件中,一般用于简单的逻辑,只在该build.gradle文件可见

//build.gradle.kts
class GreetingPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        project.task("hello") {
            doLast {
                println("Hello from the GreetingPlugin")
            }
        }
    }
}

//直接依赖build script插件
apply<GreetingPlugin>()

当然也可以将上述内容写在单独的一个gradle文件中,如other.gradle.kts,然后通过apply引入

//build.gradle.kts
//引入单独的gradle脚本文件
apply(from = "other.gradle.kts")

2、buildSrc目录

buildSrc是一个特殊的工程名称,当根工程中出现一个名为buildSrc的子工程时,gradle会将其作为一个gradle插件工程。并且该工程的插件可以被其余工程进行引用。

在buildSrc中创建插件有2种方式:脚本插件和二进制插件

2.1、脚本插件

顾名思义,就是将插件的代码写在脚本文件中,比如groovy脚本文件或gradle.kts脚本文件。并且该脚本文件需要存放在buildSrc/src/main/java/中,脚本文件名的路径名+文件名会作为这个插件的id。

比如插件脚本文件: buildSrc/src/main/java/com/test/scripts/GreetingPlugin.gradle.kts

内容如下:

package com.test.scripts

tasks.register("BuildSrcHello") {
    group = "pujh"
    doLast {
        println("Hello from the BuildSrc")
    }
}

在项目module中,通过以下命令进行依赖

//:app build.gradle.kts

apply(plugin = "com.test.scripts.GreetingPlugin")

//或者

plugins {
    id("com.test.scripts.GreetingPlugin")
}

2.2、二进制插件

二进制插件,要求我们插件定义在一个普通的代码文件中,如java、groovy、kt文件。并且要在buildSrc/mian/resources/META-INF/gradle-plugins/中创建一个properties文件,其文件名会作为这个插件的id,文件内容包含implementation-class字段,指向具体的插件类:如:

文件名:com.test.hello.properties

implementation-class=com.test.HelloPlugin

插件代码:

//com.test.HelloPlugin.kt

class HelloPlugin : Plugin<Project> {
    override fun apply(target: Project) {
        println("com.test.plugin")
    }
}

使用插件时,通过以下方式引入:

//:app build.gradle.kts

apply(plugin = "com.test.hello")

//或者

plugins {
    id("com.test.hello")
}

3、独立项目

一个独立的java项目/模块,可以将文件包发布到仓库中,然后让其余项目进行依赖。

我们创建一个名为plugins的java/kotlin模块,并在settings.gradle.kts将include修改为includeBuild。

rootProject.name = "Gradle"
include(":app")

//include(":plugins")
includeBuild("plugins") //注意这里没有冒号

此时plugins将会变成一个独立的插件工程,但此时工程中还没有任何插件,我们需要先修改工程中的build.gradle.kts

buildscript {
    repositories {
        google()
        gradlePluginPortal()
    }
    dependencies {
        classpath(kotlin("gradle-plugin", "1.9.23"))
    }
}

plugins {
    `kotlin-dsl`
    `java-gradle-plugin`
    `maven-publish`
}

repositories {
    google()
    gradlePluginPortal()
}

dependencies {
    implementation(gradleKotlinDsl())
    implementation("com.android.tools.build:gradle:8.4.1")
}

gradlePlugin {
    plugins {
        create("myplugin") {
            group = "com.test"
            id = "myplugin"
            implementationClass = "com.test.plugin.MyPlugin"
            version = "1.0"
        }
    }
}

publishing {
    repositories {
        maven(url = "../repo")
    }
}
//com.test.plugin.MyPlugin.kt

class MyPlugin : Plugin<Project> {
    override fun apply(target: Project) {
        println("com.test.plugin.MyPlugin")
    }
}

Transform

Transform在Gradle8.0中被移除

image.png