Android程序员

学习Android构建系统(三)-Gradle基础

2018-08-27  本文已影响67人  十思叶

引言


Gradle是Android构建系统的重点,需要花费时间用心学习。学习资料主要是官方的Gradle Docs

Gradle简介


Gradle是一个注重灵活性和性能的开源构建自动化工具,使用Groovy或Kotlin DSL来编写构建脚本。

虽然支持Kotlin,但还是建议学习Groovy。因为:一,目前官方示例里有许多还没有kotlin方式;二,目前大多数项目的Gradle脚本都是用Groovy编写的。

Gradle特点如下:

  1. 高度可定制。Gradle使用了可定制和可扩展的设计思想。
  2. 快速。Gradle通过重用先前执行的输出,仅处理已更改的输入以及并行执行任务来快速完成任务。
  3. 强大。Gradle是Android的官方构建工具,并支持许多主流的编程语言和技术。

Gradle支持IDE或命令行两种方式来构建工程。支持主流的IDE,如Android Studio, Eclipse, IntelliJ IDEA, Visual Studio 2017, 和XCode等。

Gradle在构建时,会根据构建脚本创建对应的Java实例,如settings.gradle对应Setting接口,build.gradle对应Project接口。

There is a one-to-one correspondence between a Settings instance and a settings.gradle settings file. Before Gradle assembles the projects for a build, it creates a Settings instance and executes the settings file against it.
There is a one-to-one relationship between a Project and a build.gradle file.

Gradle Docs的使用


Gradle Docs资料中,最重要的是User Manual(用户手册)和API文档(DSL Reference和Javadoc),其余的可以不用看。

Gradle Docs
Gradle User Manual

User Manual中最关键的一部分当属‘Build Configuration Scripts’。这一部分内容相当多,内容也比较杂。可以先看‘Build Lifecycle’和‘Configuring Multi-Project Build’这两节,看懂这两节,就可以对Gradle构建的流程有一个清晰的了解了,之后再看其他章节就非常容易了。


Gradle User Manual
API文档

API文档包括Gradle DSL Reference(Gradle特定语言指南)Javadoc(Java文档)。Reference包含主要的接口和类,并做了描述和分类以利于学习,Javadoc包含所有的API,主要用于检索。就内容的覆盖面而言,Javadoc完全包含Reference。学习时可以看Reference,使用中具体查找Gradle的某个API文档时,用Javadoc。

Reference Javadoc

几个基础概念


脚本(Script)

利用Gradle构建项目时,与开发者直接交互的就是脚本了。Gradle脚本有以下特点

  1. 每个脚本都有一个对应的代理对象。Gradle脚本是一种配置脚本,在脚本执行时,Gradle会配置特定类型对象,称为脚本的代理对象。脚本与代理对象一一对应,并且在脚本中可以直接使用代理对象的属性和方法。各Gradle脚本对应的代理对象如下:
Type of script Delegates to instance of
Build script Project
Init script Gradle
Settings script Settings
  1. Gradle脚本实现了Script接口。此接口定义了许多属性和方法,可以在脚本中直接使用。
  2. Gradle脚本由零或多个语句和脚本块组成(statements and script blocks)。 语句包括方法调用、属性赋值和局部变量定义等。 脚本块是一种方法调用,它以闭包作为方法的参数,而闭包用来配置代理对象。
项目(Project)

项目和任务是Gradle中的两个最基本的概念,所有的内容都基于它们。
每个Gradle构建都由一个或多个项目组成。 Project接口是构建文件与Gradle交互的主要API。Project实例可以访问Gradle的所有功能。

任务(Task)

项目由一个或多个任务组成。 每个任务执行一些基本工作,例如编译类、运行单元测试或压缩WAR文件等。
任务由一系列Action对象组成。 执行任务时,通过调用Action.execute(T)依次执行每个Action。 可以通过调用Task.doFirst(org.gradle.api.Action)Task.doLast(org.gradle.api.Action)向任务添加Action

以上概念详情,可查看它们的Reference

示例-单项目构建


官方示例 Creating New Gradle Builds
主要练习:初始化项目、执行任务、移动文件、添加Plugin等。

提示:示例中使用了./gradlew来执行命令,如果因为下载失败导致该命令不可用,使用gradle命令即可。

示例-多项目构建

官方示例 Creating Multi-project Builds

提示:示例中需要下载依赖,国内网络直接访问可能导致下载失败,需要设置国外代理服务。
Gradle项目代理设置如下:在电脑中设置好http协议代理后,在当前项目根目录下创建gradle.properties文件(如果有就不用创建),编辑文件如下:

systemProp.http.proxyHost=127.0.0.1
systemProp.http.nonProxyHosts=192.168.*, <localhost>
systemProp.http.proxyPort=8118
systemProp.https.proxyHost=127.0.0.1
systemProp.https.nonProxyHosts=192.168.*, <localhost>
systemProp.https.proxyPort=8118

其中8118privoxy代理的端口号
Gradle代理设置详细说明请参考官方文档Accessing the web via a proxy(虽然也不够详细:p)

示例-Android项目根目录 build.gradle语法分析


利用Android Studio创建一个项目,根目录中build.gradle文件如下

buildscript {
    ext.kotlin_version = '1.2.50'
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.4'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

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

直接使用API文档来分析上面的代码:

  1. Project和方法。build.gradle脚本的代理对象是Project实例,buildscriptallprojectstaskProject中的三个方法(实际上,task在这里是关键字,不是方法,但Project中有方法与之对应)。
  2. buildscript。在Android Studio中(Mac平台 Command+鼠标左键)或者在文档中查看API详情,如下
    buildscript
    这个方法的作用是配置项目构建脚本的classpath,参数是一个闭包,闭包的代理对象是ScriptHandler,即如果buildscript代码块中的属性和方法,如果不能在Project中找到,就去ScriptHandler中去找。
    再来分析下buildscript代码块里的内容。示例代码中,repositoriesdependencies都是ScriptHandler的方法,可以直接在Android Studio中点击查看其API。但ext是什么,是谁的属性?ownerProject)的,还是delegateScriptHandler)的?由于不能在Studio里直接查看,我们先看看Project的API
    Project
    全局搜索extsetExtgetExt,都没有结果。再看看其继承的接口,发现ExtensionAware中可以搜索到,API文档说
// All extension aware objects have a special “ext” extension of type >ExtraPropertiesExtension
assert project.hasProperty("myProperty") == false
project.ext.myProperty = "myValue"
// Properties added to the “ext” extension are promoted to the owning >object
assert project.myProperty == "myValue"

继承了ExtensionAware的对象都有一个特殊的ext扩展类型,可以直接添加属性project.ext.myProperty = "myValue",之后使用属性myProperty时可以不用写ext,如project.myProperty
因此示例代码中的extownerProject)的属性。

  1. allprojects。分析方法同上,略。
  2. taskProject中有四个task方法

Task task(String name)
Task task(String name, Closure configureClosure)
Task task(Map<String, ?> args, String name)
Task task(Map<String, ?> args, String name, Closure configureClosure)

Studio中链接的是Task task(String name),但后面的clean(type: Delete) { delete rootProject.buildDir }是什么呢?开始以为是方法,实际上并不是,一是在Project中没有clean方法,二是Groovy语法中不允许嵌套的方法省略括号

Parentheses are required for method calls without parameters or ambiguous method calls:

println(Math.max(5, 10))

不是方法,但看起来又不是参数。查看TaskAPI,有定义task的示例

You can also use the task keyword in your build file:

task myTask
task myTask { configure closure }
task myTask(type: SomeType)
task myTask(type: SomeType) { configure closure }

也就是说task可以是一个关键字!去看Gradle官方文档Defining tasks

There are a few variations on this style, which you may need to use in certain situations. For example, the keyword style does not work in expressions.

有很多方式定义task,有关键字格式:

task copy(type: Copy) {
    from(file('srcDir'))
    into(buildDir)
}

我们的示例就是这种格式的。
也有方法格式的:

task('copy', type: Copy) {
    from(file('srcDir'))
    into(buildDir)
}

这对应Project的方法Task task(Map<String, ?> args, String name, Closure configureClosure)。Groovy方法中的参数似乎是可以改变顺序的,有人写的博客中提到了这件事,但我没有找到相关的官方文档。
终于分析完了,初学者学习这些估计要抓狂,:p。

最后,把示例代码中省略的括号和ownerdelegate补全,以提高可读性,如下

buildscript({
    owner.ext.kotlin_version = '1.2.50'
    delegate.repositories({
        delegate.google()
        delegate.jcenter()
    })
    dependencies({
        delegate.add('classpath', 'com.android.tools.build:gradle:3.1.4')
        delegate.add('classpath', "org.jetbrains.kotlin:kotlin-gradle-plugin:${owner.kotlin_version}")
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    })
})

allprojects({
    delegate.repositories({
        delegate.google()
        delegate.jcenter()
    })
})

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

注意: 其中的classpath,我暂时并没有理解清楚它是如何工作的,它不是方法,只能理解它是一个可以添加的配置属性。

Dependency add(String configurationName, Object >dependencyNotation);

参考


  1. Gradle官网
  2. Groovy官网

上篇:学习Android构建系统(二)-Groovy基础
下篇:学习Android构建系统(四)-Gradle构建流程

上一篇 下一篇

猜你喜欢

热点阅读