Gradle 基本使用

前言

Gradle 它是一个基于 JVM 的新一代构建工具,这个系列会针对 Android 开发来对 Gradle 的知识进行精简讲解。Gradle 目前已经应用于多个 Android 开发的技术体系中,比如构建系统、插件化、热修复和组件化等等。

一、Gradle 是什么

Gradle 是一个构建工具,构建工具用于实现项目自动化,是一种可编程的工具,你可以用代码来控制构建流程最终生成可交付的软件。构建工具可以帮助你创建一个重复的、可靠的、无需手动介入的、不依赖于特定操作系统和IDE 的构建。这么说可能有些抽象,这里拿APK的构建过程来分析说明构建工具的作用。

Apk 的构建过程

APK 的构建过程可以根据官方提供的流程图如下图所示:

 

这个 APK 构建的过程主要分为以下几步:

  1. 通过 AAPT(Android Asset Packaging Tool) 打包 res 资源文件,比如 AndroidManifest.xml、xml 布局文件等,并将这些 xml 文件编译为二进制,其中 assets 和 raw 文件夹的文件不会被编译为二进制,最终会生成R.java 和 resources.arsc 文件。

  2. AIDL 工具会将所有的 aidl 接口转化为对应的 Java 接口。

  3. 所有的 Java 代码,包括 R.java 和 Java 接口都会被 Java 编译器编译成 .class 文件。

  4. Dex 工具会将上一步生成的 .class 文件、第三库和其他 .class 文件编译成 .dex 文件。

  5. 上一步编译生成的 .dex 文件、编译过的资源、无需编译的资源(如图片等)会被 ApkBuilder 工具打包成 APK 文件。

  6. 使用 Debug Keystore 或者 Release Keystore 对上一步生成的 APK 文件进行签名。

  7. 如果是对 APK 正式签名,还需要使用 zipalign 工具对 APK 进行对齐操作,这样应用运行时会减少内存的开销。

从以上步骤可以看出,APK 的构建过程是比较繁琐的,而且这个构建过程又是时常重复的,如果没有构建工具,手动去完成构建工作,无疑对于开发人员是个折磨,也会产生诸多的问题,导致项目开发周期变长。

(1)Gradle 是一个自动化的构建工具

Gradle 是通过组织一系列 task 来最终完成自动化构建的,所以 task 是 gradle 里最重要的概念。

(2)Gradle 脚本使用了 Groovy 或者 Kotlin 的 DSL

Gradle 使用 Groovy 或者 Kotlin 编写,不过目前还是 Groovy 居多。

那什么是 DSL 呢?DSL 也就是 Domain Specific Language 的简称,是为了解决某一类任务专门设计的计算机语言。

DSL 相对应的是 GPL (General-Purpose Language),比如 java。

与 GPL 相比起来,DSL 使用简单,定义比较简洁,比起配置文件,DSL 又可以实现语言逻辑。

对 Gradle 脚本来说,他实现了简洁的定义,又有充分的语言逻辑,以 android {} 为例,这本身是一个函数调用,参数是一个闭包,但是这种定义方式明显要简洁很多。

(3)Gradle 基于 Groovy 编写,而 Groovy 是基于 Jvm 语言

Gradle 使用 Groovy 编写,Groovy 是基于 Jvm 的语言,所以本质上是面向对象的语言,面向对象语言的特点就是一切皆对象。

所以,在 Gradle 里,.gradle 脚本的本质就是类的定义,一些配置项的本质都是方法调用,参数是后面的 {} 闭包。

比如 build.gradle 对应 Project 类,buildScript 对应 Project.buildScript 方法。

 

二、Gradle 的任务

为了更好的理解 Gradle 命令行,这里简单的介绍下 Gradle 的任务,包括创建任务、任务依赖、 动态定义任务和任务的分组和描述。

说明: 这一节的所有代码都是在Android Studio 的 Module 模块的 build.gradle 文件中编写测试。

2.1 创建任务

默认创建的 task 继承自 DefaultTask。

(1)使用 task + 任务名 方式创建

task hello{
    doLast{
        println 'hello world!'
    }
}

运行结果如下图所示:上面的代码中,task 代表一个独立的原子性操作,比如复制一个文件,编译一次 Java 代码,这里我们定义一个名为 hello 的任务。doLast 代表 task 执行的最后一个action,通俗来讲就是 task 执行完毕后会回调 doLast 中的代码。 第二种运行方式如下如所示:

在 Gradle 中找到 app ——> Tasks —— > other —— > hello。

(2)直接用任务名称创建

def Task hello = task(hello)
hello.doLast{
    println 'hello world!'
}

(3)任务名称 + 任务配置创建 (其中group为任务配置项,它代表了分组)

def Task hello=task(hello,group:BasePlugin.BUILD_GROUP)
hello.doLast{
    println "hello world"
}

(4)TaskContainer 的create 方法创建

tasks.create(name: 'hello').doLast {
    println "hello world"
}

2.2 任务依赖

任务依赖会决定任务运行的先后顺序,被依赖的任务会在定义依赖的任务之前执行。创建任务间的依赖关系如下所示。

task hello {
    doLast {
        println 'Hello task'
    }
}
task go(dependsOn: hello) {
    doLast {
        println "go task"
    }
}

在 hello 任务的基础上增加了一个名为 go 的任务,通过 dependsOn 来指定依赖的任务为 hello,因此 go 任务运行在 hello 之后。运行结果如下图所示:

2.3 动态定义任务

动态定义任务指的是在运行时来定义任务的名称,如下所示:

3.times {number ->
    task "task$number"{
        doLast{
            println "task $number"
        }
    }
}

这里用到了 Groovy 语法,times 是Groovy 在 java.lang.Number 中拓展的方法,是一个定时器。3.times 中循环创建了三个新任务,隐式变量 number 的值为 0,1,2,任务的名称由 task 加上 number 的值组成,达到了动态定义任务的目的。

同步(Sync Now)之后会创建三个task,如下图所示:

2.4 任务的描述与分组

Gradle 有任务组的概念,可以为任务配置分组和描述,以便于更好的管理任务,拥有良好的可读性,下面为 hello 任务添加分组和描述。

task hello {
    group = 'build'
    description = 'hello world'
    doLast {
        println "task group:${group}"
        println "task description:${description}"
    }
}

当给任务添加分组之后,那么任务就会在相应的分组中显示。对于hello 任务就会显示在 Gradle -> app -> Tasks -> build 中,如下图所示:

也可以使用如下方式来为任务添加分组与描述,代码如下所示:

def Task hello = task(hello)
hello.group = BasePlugin.BUILD_GROUP
hello.description = 'hello world'
hello.doLast {
    println "task group:${group}"
    println "task description:${description}"
}

2.5 Task 的方法分类

  1. Task 行为 Task.doFirst Task.doLast

  2. Task 依赖顺序 Task.dependsOn Task.mustRunAfter Task.shouldRunAfter Task.finalizedBy

  3. Task 的分组描述 Task.group Task.description

  4. Task 是否可用 Task.enabled

  5. Task 输入输出 gradle 会比较 task 的 inputs 和 outputs 来决定 task 是否是最新的,如果 inputs 和 outputs 没有变化,则认为 task 是最新的,task 就会跳过不执行 Task.inputs Task.outputs

  6. Task 是否执行 可以通过指定 Task.upToDateWhen = false 来强制 task 执行 Task.upToDateWhen

三、Gradle 日志级别

Gradle 与 Android 一样也定义了日志级别,如下表所示:

级别作用
ERROR错误消息
QUIET重要的信息消息
WARNING警告消息
LIFECYCLE进度信息消息
INFO信息性消息
DEBUG调试消息

 

四、Gradle 在 Android Studio 的项目结构分析

在 Android Studio 中新建一个工程(名称为:Gradle),如下所示:

 

4.1 settings.gradle

settings.gradle 是负责配置 module 的脚本,对应 Settings 类,gradle 构建过程中,会根据 settings.gradle 生成 Settings 的对象。

在 Android Studio 中使用 Ctrl + 鼠标左键点击 include 可以查看 Settings 源码,如下图所示:

也可以到 C:\Users\Administrator.gradle\wrapper\dists\gradle-6.5-all 目录找打Settings的源码,电脑不同可能名称会有所不同。

例如我电脑的路径如下:

C:\Users\Administrator.gradle\wrapper\dists\gradle-6.5-all\2oz4ud9k3tuxjg84bbf55q0tn\gradle-6.5\src\core-api\org\gradle\api\initialization\Settings.java

 

其中有几个主要的方法如下所示:

  1. include(projectPaths)

  2. includeFlat(projectNames)

  3. project(projectDir)

一般在项目里见到的引用子模块的方法,就是使用 include,这样引用,子模块位于根项目的下一级

include ':app'

如果想指定子模块的位置,可以使用 project 方法获取 Project 对象,设置其 projectDir 参数。

// project(':app') 得到Project对象。
project(':app').projectDir = new File('./app')

 

4.2 project/build.gradle

project 的 build.gradle 负责整体项目的一些配置,对应的是 Project 类。

gradle 构建的时候,会根据 build.gradle 生成 Project 对象,所以在 build.gradle 里写的 dsl,其实都是 Project 接口的一些方法,Project 其实是一个接口,真正的实现类是 DefaultProject 。

其中几个主要方法有:

  1. buildscript // 配置项目的 classpath

  2. allprojects // 配置项目及其子模块

  3. respositories // 配置仓库地址,后面的依赖都会去这里配置的地址查找

  4. dependencies // 配置项目的依赖

以本节所创建的Gradle项目来分析 project/build.gradle,代码如下所示:

buildscript { // 配置脚本的 classpath
    repositories { // 项目的仓库地址,会按顺序查找
        google()
        jcenter()
    }
    dependencies { // 项目的依赖
        classpath "com.android.tools.build:gradle:4.1.1"
    }
}

allprojects { // 子模块(module)的配置
    repositories {
        google()
        jcenter()
    }
}

task clean(type: Delete) { // clean 任务
    delete rootProject.buildDir
}

 

4.3 module/build.gradle

module 的 build.gradle 是子模块的配置,对应的也是 Project 类。

子项目和根项目的配置是差不多的,不过在子项目里可以看到有一个明显的区别,就是引用了一个插件 plugins { id 'com.android.application'},后面的 android dsl 就是 application 插件的 extension。

其中几个主要方法有:

  1. compileSdkVersion // 指定编译需要的 sdk 版本

  2. defaultConfig // 指定默认的属性,会运用到所有的 variants 上

  3. buildTypes // 一些编译属性可以在这里配置

  4. productFlavor // 配置项目的 flavor

以 app 模块的 build.gradle 来分析module/build.gradle,代码如下所示:

// 引入 android gradle 插件
plugins { id 'com.android.application'}

android { // 配置 android gradle 插件需要的内容
    compileSdkVersion 30
    buildToolsVersion "30.0.2"

    defaultConfig { // 默认配置
        applicationId "com.lx.gradle"
        minSdkVersion 16
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {// 指定编译属性
        release { 
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {// 指定java的版本
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    // flavor 相关配置
    flavorDimensions "size", "color"
    productFlavors {
        big {
            dimension "size"
        }
        small {
            dimension "size"
        }
        blue {
            dimension "color"
        }
        red {
            dimension "color"
        }
    }
}

dependencies { // module 所需要的依赖
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.2.1'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

 

4.4 Gradle Wrapper

gradlew / gradlew.bat

这两个文件用来下载特定版本的 gradle 然后执行的,就不需要开发者在本地再安装 gradle 了。

gradle/wrapper/gradle-wrapper.properties

是一些 gradlewrapper 的配置,其中用的比较多的就是 distributionUrl,可以执行 gradle 的下载地址和版本。

gradle/wrapper/gradle-wrapper.jar

是 gradle wrapper 运行需要的依赖包。

 

五、Gradle 生命周期

gradle 构建分为三个阶段

1. 初始化阶段

初始化阶段主要做的事情是有哪些项目需要被构建,然后为对应的项目创建 Project 对象。

2. 配置阶段

配置阶段主要做的事情是对上一步创建的项目进行配置,这时候会执行 build.gradle 脚本,并且会生成要执行的 task。

3. 执行阶段

执行阶段主要做的事情就是执行 task,进行主要的构建工作。

gradle 在构建过程中,会提供一些列回调接口,方便在不同的阶段做一些事情,主要的接口有下面几个

gradle.addBuildListener(new BuildListener() {
    @Override
    void buildStarted(Gradle gradle) {
        // 这个回调一般不会调用,因为我们注册的时机太晚,注册的时候构建已经开始了,是 gradle 内部使用的
        println('Build start')
    }

    @Override
    void settingsEvaluated(Settings settings) {
       // Settings 文件解析完成
        println('The Settings file is resolved')
    }

    @Override
    void projectsLoaded(Gradle gradle) {
        // 项目加载完成
        println('Project loading completed')
        gradle.rootProject.subprojects.each {
            pro ->
                pro.beforeEvaluate {
                    // 项目配置之前调用
                    println("${pro.name} before")
                }
                pro.afterEvaluate {
                    // 项目配置之后调用
                    println("${pro.name} after")
                }
        }
    }

    @Override
    void projectsEvaluated(Gradle gradle) {
        // 项目解析完成
        println('Project analysis completed')
    }

    @Override
    void buildFinished(BuildResult result) {
        // 构建完成
        println('Build finish')
    }
})

gradle.taskGraph.whenReady {
    // task 图构建完成
    println("task graph build finish")
}
gradle.taskGraph.beforeTask {
    // 每个 task 执行前会调这个接口
    println("exec task, before")
}
gradle.taskGraph.afterTask {
    // 每个 task 执行完成会调这个接口
    println("exec task, after")
}

 

六、自定义插件

gradle 的插件可以看作是一系列 task 的集合。

在 android 工程的 build.gradle 脚本里,开头就是 plugins { id 'com.android.application' },这个就是引入 android gradle 插件,插件里有 android 打包相关的 task。

插件开发可以使用 Groovy 和 Java 语言,这里使用 Java 语言开发,因为 Java语言对于 Android 开发者来说更熟悉。

现在先看看如何实现一个自己的 Plugin

6.1 初始化工程

1)在 android studio 中创建一个 Java Module 工程,名称为 ”plugin-lib“,包名为 "com.lx.plugin",默认创建一个MyPlugin类,如下图所示:

 

2)在 src/main 目录下创建 resources/META-INF/gradle-plugins 目录,创建 myplugin.properties 文件 (说明:定义的 xxx.properties 文件,而 xxx 就是 apply plugin 时候使用的 id),里面声明插件的入口类,文件里内容如下所示:

# com.lx.plugin.MyPlugin 是自己定义的插件类
implementation-class=com.lx.plugin.MyPlugin

3)修改 plugin-lib 的 build.gradle 文件,代码如下所示:

// 引入 groovy 和 java-library 插件
plugins {
    id 'java-library'
    id 'groovy'
}
​
java {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
}
​
​
dependencies {
    implementation gradleApi()
}

到此,plugin-lib 的目录结构如下图所示:

6.2 创建插件

在刚才创建的插件类里,就可以写插件的代码了。插件类继承 Plugin,并实现 apply 接口,apply 就是在 build.gradle 里 apply plugin 'xxx' 的时候要调用的接口了,代码如下所示:

package com.lx.plugin;

import org.gradle.api.Plugin;
import org.gradle.api.Project;

/**
 * create by lx
 * date 2020/12/4.
 * description:自定义插件
 */
public class MyPlugin implements Plugin<Project> {
    @Override
    public void apply(Project target) {
        System.out.println("apply my plugin");
    }
}

 

6.3 创建插件的 Task

我们再定义一个 task 类 MyTask,继承自 DefaultTask,代码如下所示:

package com.lx.plugin;

import org.gradle.api.DefaultTask;
import org.gradle.api.tasks.TaskAction;

/**
 * create by lx
 * date 2020/12/4.
 * description:task任务
 */
public class MyTask extends DefaultTask {

    @TaskAction
    void action() {
        System.out.println("my task exec");
    }
}

接下来在 plugin 中注册这个 Task

public class MyPlugin implements Plugin<Project> {
    @Override
    public void apply(Project target) {
        System.out.println("apply my plugin");
        // 注册 MyTask 任务
        target.getTasks().create("myTask", MyTask.class);
    }
}

 

6.4 安装本地插件

我们首先需要在 plugin-lib 的 build.gradle 中引入 maven 插件,并且配置 install 相关的属性,代码如下所示:

plugins {
    ...
    id 'maven'
}

install {
    repositories.mavenInstaller {
        pom.version = '1.0.1' // 配置插件版本号
        pom.artifactId = 'myplugin' // 配置插件标识
        pom.groupId = 'com.lx.plugin' // 配置插件组织
    }
}

...

之后执行 gradlew install 便会把插件安装在本地 maven 仓库。

然后需要使用插件的地方引入插件的 classpath ,例如在 Gradle 的 build.gradle 中使用插件,添加如下代码:

buildscript { // 配置脚本的 classpath
    repositories { // 项目的仓库地址,会按顺序查找
        google()
        jcenter()
        mavenLocal() // 本地maven仓库

    }
    dependencies { // 项目的依赖
        classpath "com.android.tools.build:gradle:4.1.1"
        // 添加插件
        classpath 'com.lx.plugin:myplugin:1.0.1'

    }
}

allprojects { // 子模块(module)的配置
    repositories {
        google()
        jcenter()
        mavenLocal()
    }
}

task clean(type: Delete) { // clean 任务
    delete rootProject.buildDir
}

最后在 app 的 build.gradle 中加载插件,代码如下所示:

plugins {
    id 'com.android.application'
    // 添加自定义插件
    id 'myplugin'
}

然后 “Sync Now” 同步一下就可以在 Gradle -> app -> Tasks -> other 中看到 myTask 任务了。

  点击上图中的 myTask 运行此任务,结果如下图所示:

说明:在Android Studio 中开发插件调试方法与开发普通项目一样,可以使用断点调试,如下图所示:

 

扫描下方二维码关注公众号,获取更多技术干货。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值