初识Gradle

前言

Gradle是Android工程师的进阶必备,最近我也开始慢慢了解Gradle。

本文不会花太多的篇幅介绍Groovy语法,主要是介绍Gradle的基本概念,和一些常见的用法。

先给大家安利一下官方文档吧Gradle User Manual,如果英语不错的话我感觉看官方文档可能更好,因为本文的主要内容也来自官方文档。

Groovy

基本概念

Groovy是一种动态语言。这种语言比较有特点,它和Java一样,也运行于Java虚拟机中。你可以认为Groovy扩展了Java语言

比如,Groovy对自己的定义就是:Groovy是在 java平台上的、 具有像Python, Ruby 和 Smalltalk 语言特性的灵活动态语言, Groovy保证了这些特性像 Java语法一样被 Java开发者使用。

实际上,由于Groovy Code在真正执行的时候已经变成了Java字节码,所以JVM根本不知道自己运行的是Groovy代码。

闭包

Groovy的基本语法就不介绍了,可以参看任玉刚Gradle从入门到实战 - Groovy基础,这里简单聊一下闭包。

官方定义:

A closure in Groovy is an open, anonymous, block of code that can take arguments, return a value and be assigned to a variable.

Groovy中的闭包是一段开放的、匿名的代码块,并且可以带参数,返回值,也可以赋值给一个变量。

简而言之,闭包就是一段代码块

看如下代码:

task hello {
    doLast {
        println 'Hello, My First task!
    }
}

以上代码是简写,省略了圆括号,其实应该是:

task hello {
    doLast ({
        println 'Hello, My First task!
    })
}

这里的闭包就是;

//闭包是一段代码,所以需要用大括号括起来
{
        println 'Hello, My First task!
}

如上代码的意思是定义了一种task叫hello(task是什么,下文会讲到),doLast就是把一个闭包对象传递给了这个task,在task的最后阶段会执行这个闭包中的代码println 'Hello, My First task!

有了圆括号,你会知道doLast只是把一个Closure对象传了进去。很明显,它不代表这段脚本解析到doLast的时候就会调用println 'Hello, My First task! 。但是把圆括号去掉后,就感觉好像println 'Hello, My First task!立即就会被调用一样!

Gradle

Gradle简介

Gradle是一个基于JVM的构建工具,支持传递性依赖管理,是一款通用灵活的构建工具。面向Java应用为主,当然也支持Groovy、Kotlin、Scala等等。

Project

Gradle中,每一个待编译的工程都叫一个Project。

先来看我的一个Android工程结构:
这里写图片描述

我的Android工程叫AndroidStudy,它是一个Project。我的项目中还有两个module,app和buildsrc,每个module下都有一个build.gradle文件。这里的每一个module对于Gradle来说也是一个Project,而build.gradle文件就是该Project的编译脚本

所以这里有一个主Project–AndroidStudy和两个子Project,app和buildsrc

在命令行中输入:

gradle projects

查看工程中的Project,得到如下结果:

Root project ‘AndroidStudy’
+— Project ‘:app’
— Project ‘:buildsrc’

我们在主工程下,都有一个setting.gradle文件,它的作用是什么呢,它就是告诉Gradle构建系统,这个主工程一共有多少个子工程需要编译。例如,AndroidStudy项目下的setting.gradle内容是这样的:

include ':app', ':buildsrc'

Task

每一个Project在构建的时候都包含一系列的Task。比如一个Android APK的编译可能包含:Java源码编译Task、资源编译Task、JNI编译Task、lint检查Task、打包生成APK的Task、签名Task等。

我们可以在命令行输入:

gradle tasks

去查看项目下有多少Task,得到的结果就像这样:

Android tasks
-------------
androidDependencies - Displays the Android dependencies of the project.
signingReport - Displays the signing info for each variant.
sourceSets - Prints out all the source sets defined in this project.

Build tasks
-----------
assemble - Assembles all variants of all applications and secondary packages.
assembleAndroidTest - Assembles all the Test applications.
assembleDebug - Assembles all Debug builds.
assembleRelease - Assembles all Release builds.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
classes - Assembles main classes.
clean - Deletes the build directory.
cleanBuildCache - Deletes the build cache directory.
compileDebugAndroidTestSources
compileDebugSources
compileDebugUnitTestSources
compileReleaseSources
compileReleaseUnitTestSources
jar - Assembles a jar archive containing the main classes.
mockableAndroidJar - Creates a version of android.jar that's suitable for unit tests.
testClasses - Assembles test classes.

Build Setup tasks
-----------------
init - Initializes a new Gradle build.
wrapper - Generates Gradle wrapper files.

CreateActivity tasks
--------------------
createActivity
……

显然Task还是有分组的。你当然也可以点击AS右上角的按钮来查看Task:
这里写图片描述

我们常用的Task有哪些呢,比如clean,build。我们执行对应Task,其实只要在命令中进行输入:

gradle clean
gradle build

和你点击AS上对应的按钮是一样的效果。

自定义Task

例1:

task originalInputs {
    doLast {
        file('inputs').mkdir()
        file('inputs/1.txt').text = "Content for file 1."
        file('inputs/2.txt').text = "Content for file 2."
        file('inputs/3.txt').text = "Content for file 3."
    }
}

这里是一个自定义Task,叫originalInputs,在这个Task执行的最后,会执行一些文件创建操作。

  • 一个Task包含若干Action。所以,Task有doFirst和doLast两个函数,用于添加需要最先执行的Action和需要和需要最后执行的Action。Action就是一个闭包

例2:

task copyTaskWithPatterns(type: Copy) {
    from 'A'
    into 'B'
    include '**/*.html'
    include '**/*.jsp'
    exclude { details ->
        details.file.name.endsWith('.html') &&
                details.file.text.contains('staging')
    }
}

这里的自定义Task叫copyTaskWithPatterns,我们可以理解成创建了一个Copy Task的对象,并指定了一些参数。这里的操作就是,从A文件拷贝一些东西到B文件,并且对拷贝的内容进行了过滤。

  • Task创建的时候可以指定Type,通过type:名字的形式。这是什么意思呢?其实就是告诉Gradle,这个新建的Task对象会从哪个基类Task派生。比如,Gradle本身提供了一些通用的Task,最常见的有Copy Task。Copy是Gradle中的一个类。当执行task copyTaskWithPatterns(type: Copy)的时候,创建的Task就是一个Copy Task。

例3:

class HelloTask extends DefaultTask {
    String nameOfPerson = "David"

    @TaskAction
    void hello() {
        println "Hello, $nameOfPerson !"
    }
}

如上,我们也可以在.java或者.groovy文件中声明一个Task,但是我们并不能直接调用这个Task。还是要在build.gradle中创建它的一个对象:

task helloMike(type: com.example.tsnt.task.HelloTask) {
    nameOfPerson = "Mike"
}

最后执行helloMike这个Task,控制台会输出:

Hello, Mike !

Plugin

一个Project到底包含多少个Task,其实是由编译脚本指定的插件决定。插件是什么呢?插件就是用来定义Task,并具体执行这些Task的东西。

Gradle是一个框架,作为框架,它负责定义流程和规则。而具体的编译工作则是通过插件的方式来完成的。比如编译Java有Java插件,编译Groovy有Groovy插件,编译Android APP有Android APP插件,编译Android Library有Android Library插件。

加载插件就像这样:

apply plugin: 'com.android.application'

或者:

apply plugin: 'groovy'

再或者:

apply plugin: 'java'

自定义Plugin

首先需要实现Plugin接口,然后重写apply()方法,如下:

class FirstPlugin implements Plugin<Project> {
    @Override
    void apply(Project project) {
        System.out.println("====================");
        System.out.println("Hello, FirstPlugin!");
        System.out.println("====================");

        project.tasks.create('ballTask') {
            println("ballTask in configuration!")
            doLast {
                println 'ballTask in execution!'
            }
        }
    }
}

apply()会在我们加载插件的时候被调用,传入的参数project就代表加载该插件的Project

这里我自定义了一个叫FirstPlugin的插件,我在apply()里做的操作很简单,打印了3行文字,然后动态地创建了一个Task叫ballTask

我刚才提到传入的参数project就代表加载该插件的Project,那我们就能拿到这个Project对象中的属性。

再举个例子:

// 首先我创建了一个类GreetingPluginExtension,定义了两个属性。
class GreetingPluginExtension {
    String message
    String greeter
}

// 然后我创建了一个插件GreetingPlugin
class GreetingPlugin implements Plugin<Project> {
    void apply(Project project) {
        // 我拿到当前Project中的greeting,创建一个GreetingPluginExtension对象extension,用来保存greeting中的值
        def extension = project.extensions.create('greeting', GreetingPluginExtension)
        // 然后我动态地去创建一个叫hello的Task
        project.task('hello') {
            doLast {
                // 在Task执行的最后阶段,去打印extension中保存的greeting中取得的值
                println "${extension.message} from ${extension.greeter}"
            }
        }
    }
}

// 最后来看一下我在build.gradle中定义的greeting
greeting {
    message = 'Hi'
    greeter = 'Gradle'
}

然后执行hello这个Task,会发现控制台输出:

Hi from Gradle

应该和你脑海中的结果一样把。

Gradle的生命周期

一共有三个阶段:

初始化阶段:Gradle构建系统会根据setting.gradle中的内容,知道有哪些Project会需要编译,并且为每个Project创建一个Project对象。

配置阶段:在这个阶段,Gradle构建系统会执行每个加入编译的Project下的build.gradle脚本。

执行阶段:根据你所要执行的Task的依赖和参数,Gradle构建系统就会将这个Task链上的所有Task全部按依赖顺序执行一遍。

如下是我的一个gradle脚本中的部分内容,帮助你理解Gradle的生命周期:

println 'This is executed during the configuration phase.'

task taskInConfiguration {
    println 'This is also executed during the configuration phase.'
}

task taskInExecution {
    doLast {
        println 'This is executed during the execution phase.'
    }
}

task taskInBoth {
    // 在运行阶段, 最先执行
    doFirst {
        println 'This is executed first during the execution phase.'
    }

    // 在运行阶段, 最后执行
    doLast {
        println 'This is executed last during the execution phase.'
    }

    // 配置阶段执行
    println 'This is executed during the configuration phase as well.'
}

我去执行taskInBoth,最后执行结果如下:

Configure project :buildsrc
This is executed during the configuration phase.
This is also executed during the configuration phase.
This is executed during the configuration phase as well.
Task :buildsrc:taskInBoth
This is executed first during the execution phase.
This is executed last during the execution phase.

提示

  • 可以去gradle目录下的samples目录下查看官方给出的Task和Plugin的例子。

  • 定义Plugin或者Task可以选择直接用Java,也可以选择用Groovy,记得Groovy是Java的扩展

  • 文中所举的例子,可以在AndroidStudy中看到。

参考

  1. Gradle User Manual
  2. Gradle API
  3. 深入理解Android之Gradle
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值