gradle设置build执行命令_GRADLE脚本的语法和BUILD流程

本文详细介绍了Gradle脚本的构建流程,包括Groovy和脚本的关系、Gradle脚本对象的代理机制、Gradle的构建阶段,以及Settings和Build脚本的实例分析。通过一个包含app和library子工程的demo,展示了Settings脚本的include方法以及Build脚本中apply plugin、android{}块的配置细节。文章还探讨了Groovy的Map、闭包等相关语法,以及在Gradle中如何查找和应用方法。

导语

Android Studio中使用了Gradle进行build。我阅读了groovy官方文档,Gradle官方文档及一些源代码,Android插件的官方文档及一些源代码,希望给大家介绍一下Gradle脚本的语法和处理流程。简单Groovy是一种运行在JVM上的语言, Gradle是使用Groovy实现的, Android插件提供了android特定的功能。

1. Gradle脚本的build流程

1.1Groovy和脚本

Groovy会把脚本编译成groovy.lang.Script的子类。groovy.lang.Script是一个抽象类,它有一个抽象方法run(). 如果有一个脚本的文件名是Main,它的内容是:

println 'Hello from Groovy'

它编译后生成的类是:

class Main extends groovy.lang.Script {

def run() {

println 'Hello from Groovy'

}

static void main(String[] args) {

InvokerHelper.runScript(Main, args)

}

脚本中的语句会成为run方法的实现。

Gradle脚本编译生成的类当然也继承自groovy.lang.Script,并同时实现了Gradle自己的script接口org.gradle.api.Script。

1.2 Gradle脚本对象的代理对象

每一个Gradle脚本对象都有一个代理对象。Settings脚本的代理对象是Setting对象,Build脚本的代理对象是Project对象。每一个在脚本对象未找到的属性和方法都会转到代理对象。

Groovy和Java的一个不同之处就是Groovy可以动态的添加方法和属性。动态添加的方式之一是覆盖propertyMissing和methodMissing方法,Gradle就是采用这种方式实现了脚本对象的代理对象。下面是Gradle脚本对象的基类BasicScript的实现代码片段:

public abstract class BasicScript extends org.gradle.groovy.scripts.Script implements org.gradle.api.Script, FileOperations, ProcessOperations {

......

private Object target;

private DynamicObject dynamicTarget;

......

public Object propertyMissing(String property) {

if ("out".equals(property)) {

return System.out;

} else {

return dynamicTarget.getProperty(property);

}

}

......

public Object methodMissing(String name, Object params) {

return dynamicTarget.invokeMethod(name, (Object[])params);

}

}

1.3 Gradle脚本的build流程

Gradle脚本的build流程分为3个阶段:

(1)初始化阶段

Gradle支持单个和多个工程的编译。在初始化阶段,Gradle判断需要参与编译的工程,为每个工程创建一个Project对象,并建立工程之间的层次关系。这个阶段执行Settings脚本。

(2)配置阶段

Gradle对上一步创建的Project对象进行配置。这个阶段执行Build脚本

(3)执行阶段

Gradle执行选中的task。

1.4一个demo

下面是demo工程的文件层次,这个demo会被后面的部分使用。这个例子包含一个app子工程和一个library子工程。settings.gradle是Setttings脚本,三个build.gradle都是Build脚本。

--settings.gradle

--build.gradle

--app

--build.gradle

--mylibrary

--build.gradle

2. Settings脚本

2.1 Settings脚本的内容

Settings脚本通常比较简单,用于初始化project树。demo中settings.gradle的内容是:

include ':app', ':mylibrary'

2.2 groovy的相关语法

groovy允许省略语句结尾的分号,并允许在方法调用时省略括号,上面的代码等价于:

include(':app', ':mylibrary');

2.3 Gradle的相关语法

初始化脚本的Script对象会有一个Project代理对象。在Script对象没有定义的属性和方法调用就会被转到Project对象。上面的语句实际上调用的是Project对象的include方法,该方法的原型如下:

void include(String[] projectPaths)

这个方法将给定的工程添加到build中。工程路径的格式是: 以一个可选的”:”的开始,它表示”:”前面有一个不需要名字的根工程;剩下的部分是以”:”分隔的工程名。例如, “:app”中”:”的是可选的,它表示”:”前面有一个不需要名字的根工程。

运行”gradle projects”可以获得这个demo的project树:

Root project 'AndroidGradleDemo'

+--- Project ':app'

\--- Project ':mylibrary'

3. Build脚本

3.1 app工程的Build脚本

apply plugin: 'com.android.application'

android {

compileSdkVersion 23

......

buildTypes {

debug {

applicationIdSuffix ".debug"

}

release {

minifyEnabled false

//getDefaultProguardFile() will return the full path of 'proguard-android.txt'

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

}

}

}

.......

3.2 Groovy相关的语法

1)Map

Groovy可以直接使用Java中的各种集合类型,例如Map和List等等。在初始化语法上有一些扩展

def key = 'name'

//创建一个Map。注意"Guillaume"的key是字符串"key"而不是变量key的值

def person = [key: 'Guillaume']

assert !person.containsKey('name')

assert person.containsKey('key')

//创建一个Map。我们使用()包围了key。这种情况下,"Guillaume"的key是变量key的值"name"

def person2 = [(key): 'Guillaume']

assert person2.containsKey('name')

assert !person2.containsKey('key')

(2)闭包

(2.1)Syntax

Groovy中的闭包是一个匿名的代码块。它的语法规则是:

{ [closureParameters -> ] statements }

)[closureParameters→]是可选的以”,”分隔的参数列表

*)statements是0或多条Groovy语句

下面是闭包的一些例子:

{ item++ }

//A closure using an implicit parameter (it)

{ println it }

//In that case it is often better to use an explicit name for the parameter

{ it -> println it }

{ String x, int y ->

println "hey ${x} the value is ${y}"

}

{ reader ->

def line = reader.readLine()

line.trim()

}

(2.2)Owner, delegate and thisGroovy中的闭包有三个重要的概念: Owner, delegate and this

*)this是指定义闭包的类

*)Owner是指直接包含闭包的类或闭包

*)delegate是指用于解析闭包中属性和方法调用的第三方对象

下面的代码段说明了this和闭包的区别:

class Enclosing {

void run() {

def whatIsOwnerMethod = { getOwner() }

//calling the closure will return the instance of Enclosing where the the closure is defined

assert whatIsOwnerMethod() == this

def whatIsOwner = { owner }

assert whatIsOwner() == this

}

}

class NestedClosures {

void run() {

def nestedClosures = {

def cl = { owner }

cl()

}

//then owner corresponds to the enclosing closure, hence a different object from this!

assert nestedClosures() == nestedClosures

}

}

下面的代码演示了delegate的作用。

class Person {

String name

}

def p = new Person(name:'Igor')

def cl = { name.toUpperCase() }

cl.delegate = p

//在设置了delegate后,闭包中的name属性被解析成作为delegate的Person的属性

assert cl() == 'IGOR'

Gradle中大量使用闭包的delegate来实现对各种对象的配置。

(3)转化成Java语法后的代码

Map map = new HashMap();

map.put("plugin", "com.android.appliaction");

apply(map);//这个方法会代理给Project对象

android({

//这个闭包的delegate是Android插件添加的extension

compileSdkVersion(23);

......

buildTypes({

//这个闭包的delegate是 NamedDomainObjectContainerConfigureDelegate.

debug({

applicationIdSuffix(".debug")

});

release({

minifyEnabled(false);

//getDefaultProguardFile() will return the full path of 'proguard- android.txt'

proguardFiles(getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro');

});

});

});

3.3 Gradle的相关语法

(1)Project查找方法的规则

Build脚本的代理对象是Project对象。Project对象从6个范围中查找方法:

*)Project对象本身定义的方法

*)脚本文件中定义的方法

*)被插件添加的extension. extension的名字可以做为方法名

*)被插件添加的convension方法。

*)工程中的task。task的名字可以作为方法名

*)父工程中的方法。

(2)apply plugin: ‘com.android.application’

“apply plugin: ‘com.android.application’”等价于调用Project对象的apply方法,该方法的原型是:

void apply(Map options)

Project.apply方法用于应用脚本和插件。我们指定了键值对”plugin”:”com.android.application”, 因此这个调用会添加Android插件。Android插件会添加一个名字是”android”,类型是com.android.build.gradle.AppExtension的extension.

(3)android{…}

这个代码块等价于”android({…});”, 即调用了一个方法名是”android”的方法,该方法的参数是一个闭包。当调用Project对象的android方法时,实际上是找到了名字是”android”的extension, 把这个extension设为闭包的代理并运行这个闭包。从而闭包中的方法调用实际上都是对com.android.build.gradle.AppExtension的调用。这就是上面提到的Project查找方法的规则中的一条:被插件添加的extension的名字可以做为方法名。

*)查找extension的逻辑是在ExtensionsStoage.configureExtension中,代码如下

public T configureExtension(String methodName, Object ... arguments) {

Closure closure = (Closure) arguments[0];

ClosureBackedAction action = new ClosureBackedAction(closure);

//根据名字查找extension

ExtensionHolder extensionHolder = extensions.get(methodName);

return extensionHolder.configure(action);//Line 69

}

*)设置闭包的delegate并运行闭包的逻辑是在ClosureBackedAction.execute中, 代码如下:

public void execute(T delegate) {

if (closure == null) {

return;

}

try {

if (configureableAware && delegate instanceof Configurable) {

((Configurable) delegate).configure(closure);

} else {

Closure copy = (Closure) closure.clone();

copy.setResolveStrategy(resolveStrategy);

//设置delegate

copy.setDelegate(delegate);

if (copy.getMaximumNumberOfParameters() == 0) {

copy.call();

} else {

//运行闭包

copy.call(delegate);//

}

}

} catch (groovy.lang.MissingMethodException e) {

if (Objects.equal(e.getType(), closure.getClass()) && Objects.equal(e.getMethod(), "doCall")) {

throw new InvalidActionClosureException(closure, delegate);

}

throw e;

}

}

(4)buildTypes{…}

buildTypes{…}位于android{…}代码块中,它等价于AppExtension的buildTypes方法,该方法的参数是一个闭包。AppExtension中定义了一个buildTypes方法,代码如下:

void buildTypes(Action super NamedDomainObjectContainer> action) {

plugin.checkTasksAlreadyCreated();

action.execute(buildTypes)

}

buildTypes的闭包的delegate是一个NamedDomainObjectContainerConfigureDelegate类型的实例,因此该闭包内部的方法都会被delegate到这个对象,诸如”debug”方法,”release”方法,或者其他的以用户自定义的build type作为名字的方法。相应的代码是在NamedDomainObjectContainerConfigureDelegate的基类ConfigureDelegate的invokeMethod中,代码如下:

public Object invokeMethod(String name, Object paramsObj) {

......

//对已经创建过的build type进行配置

_delegate.invokeMethod(name, result, params);

if (result.isFound()) {

return result.getResult();

}

MissingMethodException failure = null;

if (!isAlreadyConfiguring) {

// Try to configure element

try {

//创建新的build type并进行配置

_configure(name, params, result);

} catch (MissingMethodException e) {

// Workaround for backwards compatibility. Previously, this case would unintentionally cause the method to be invoked on the owner

// continue below

failure = e;

}

if (result.isFound()) {

return result.getResult();

}

}

}

*)对于已经创建过的build type,调用_delegate.invokeMethod(),进而调用DefaultNamedDomainObjectCollection$ContainerElementsDynamicObject.invokeMethod()进行配置

*)对于需要创建的build type,调用_configure(), 进而调用AbstractNamedDomainObjectContainer.create(String name, Closure configureClosure)进行创建和配置

如果大家喜欢,请不吝打赏!!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值