从宏观的角度看 Gradle 的工做过程

本文默认读者使用 macOShtml


以我本身为例,刚开始接触 Android 开发的时候,只是对 Java 开发有一些了解,对于 Android 开发的整个生态和技术栈只有一个模糊的认知。 缺少对 Gradle 的认知,在初级开发阶段可能勉强能够应付,只须要知道如何添加第三方依赖,如何调整 android block 中的配置基本上就够了,可是随着项目结构愈来愈复杂,默认提供的构建过程渐渐不能知足开发的需求,此时就要求开发者对 Gradle 的构建过程和原理有更深刻的了解,便于自定义个性化的构建过程。java

在阅读完 《Gradle In Action》后,我发现 Android 工程的构建过程彷佛再也不那么神秘了。 本文但愿能从宏观的角度带刚接触 Android 开发不久的同窗认识一下 Gradle,若是对 Gradle 工做原理感兴趣,但愿可以更加深刻了解,建议阅读 《Gradle In Action》 这本书。android

背景

随着项目规模增大,软件工程师须要考虑的事情会愈来愈多。成功构建并运行一个项目再也不像单文件的 HelloWorld 同样简单。随着持续集成思想的普及,一次成功的构建可能分为 checkStyle,Lint,编译,单元测试,集成测试,代码裁剪,代码混淆,打包部署等多个步骤。若是项目中引用了第三方 lib,那么第三方 lib 会有版本迭代,甚至多个第三方 lib 可能又依赖了不一样版本的同一个第三方 lib,形成依赖版本冲突,事情会愈来愈复杂。咱们须要使每个 Commit 老是能构建出彻底相同的结果,Git 对于二进制文件的版本管理又不是那么驾轻就熟,手动构建经常会引入人为变数致使构建出错。因此构建过程自动化迫在眉睫。git

常见的 Java 构建工具

  • Ant (Anothre Neat Tool) 2000年
    • 使用 XML 描述构建的步骤
    • 只负责构建步骤管理,若是要添加依赖管理的功能,还须要引入 Ivy
  • Maven 2004年
    • convention over configuration 的思想,无需配置或者仅需少许配置便可开始构建
    • 和 Ant 对比增长了依赖库管理
  • Gradle 2007年
    • 使用 Groovy DSL 替代繁琐的 XML
    • 支持增量构建
    • 项目结构更加灵活

Google 基于 Gradle 经过 Android Gradle Plugin 提供了自动化构建的工具,对开发者隐藏了大量的繁琐的构建过程,暴露一些可被开发者配置的属性,大大的简化了 Android 项目管理的复杂度的同时又不失灵活性。web

在这里列举的构建工具不止能够用来构建 Java 相关的项目。只要能表达出构建步骤,就可使用这些工具来进行项目构建。好比,你可使用 Gradle 来构建一个 iOS 的项目。api

Gradle Wrapper:

The Wrapper is a script that invokes a declared version of Gradle, downloading it beforehand if necessary. As a result, developers can get up and running with a Gradle project quickly without having to follow manual installation processes saving your company time and money.bash

docs.gradle.org/current/use…网络

构建工具也是须要版本迭代的,一个大的版本迭代可能不会提供向前的兼容性,也就是说,在 A 机器上和 B 机器上装了两个不一样版本的 Gradle,结果可能致使同一个项目,在 A 的机器上能够成功构建,而在 B 的机器上会构建失败。 为了不这个问题,保证每一个 Commit 总能构建出彻底相同的结果。Gradle 提供了 Gradle Wrapper,经过 Wrapper 运行 Gradle Task 的时候,会先检查 gradle-wrapper.properties 中指定的位置下,指定版本的 Gradle 是否安装,若是已经安装,则将该 Gradle Task 交给 Gradle 处理。若是没有安装,则先下载安装指定版本的 Gradle,而后再将 Gradle Task 交给 Gradle 处理。 gradlew 是一个 script,是 Gradle Wrapper 的入口,Windows 下是 gradlew.bat。 gradle-wrapper.jar 提供了 Gradlew Wrapper 的核心功能。oracle

目录结构以下图: app

图片

以下图所示是一个典型的使用 Gradle 进行构建的 Android 工程。 工程中包含两个 Project:

  1. TutorialAndroid -- RootProject
  2. app -- SubProject

图片
可使用以下命令查看工程中的 Project

gradlew projects
复制代码

gradlew 是入口 Script, projects 其实是 Gradle 一个内置的 Task。 关于 Task 的概念,下面再解释。 运行上面的命令,结果以下图所示,能够看到,通常咱们开发时修改 **app **只是一个子项目,RootProject 其实是 app 的上级目录中的 TutorialAndroid。

图片

构建过程

Gradle 的构建过程分为如下几个阶段: initialization -> configuration -> execution

  1. initialization phase
    • Gradle 使用 Project 对象来表示项目,在 initialization 阶段,Gradle 会为每一个参与本次构建的项目建立一个 Project 对象。
    • 由于 Gradle 支持多项目构建,因此在初始化阶段的时候,须要判断哪些项目须要参与本次构建。
    • Gradle 能够从 Project 的根目录开始构建,也能够从任意包含 build file 的子文件架开始构建。不管从哪里开始构建,Gradle 都须要知道有哪些 Project 须要参与构建,Root Project 的 settings.gradle 中声明了须要参与构建的 Project 的信息。因此 Gradle 在这个阶段作的事情,就是从当前目录开始,逐级向上搜索 settings.gradle ,若是找到了,就按照 settings.gradle 中声明的信息设置本次构建,若是最终没有找到,那么就默认只有当前所在的 Project 须要参与本次构建。
  2. configuration phase

    A Task represents a single atomic piece of work for a build, such as compiling classes or generating javadoc.

    A Task is made up of a sequence of Action objects. When the task is executed, each of the actions is executed in turn, by calling Action.execute(T). You can add actions to a task by calling Task.doFirst(org.gradle.api.Action) or Task.doLast(org.gradle.api.Action).

    docs.gradle.org/current/dsl…

    • Task 属于 Project 对象。能够在 build.gradle 文件中简单定义 Task
    // 定义好 Task 以后,就能够经过 `gradlew simpleTask` 来运行指定的 Task
     task simpleTask {
         doLast {
         println "This is a simple task."
         }
     }
    复制代码
    • 项目构建过程分为不少步骤,在 Gradle 中用 Task 来表示这些步骤,Task 之间可能有依赖关系,例如:必须先执行完 compile Task,才能执行 unitTest Task。在 configuration 阶段,Gradle 会分析 Task 之间的依赖关系,配置初始化阶段建立的 Project 对象。

    Gradle determines the subset of the tasks, created and configured during the configuration phase, to be executed. The subset is determined by the task name arguments passed to the gradle command and the current directory.

    docs.gradle.org/current/use…

    • 当一个 Project 的 Task 愈来愈复杂,或者多个项目都须要共用同一个 Task 的时候,为了提升代码复用性,能够编写 Plugin 将建立 Task 等逻辑封装起来。

      图片
      build.gradle 中,如图所示就是在使用封装好的 Plugin。

    • 提升了代码复用性的同时,还须要提供足够的灵活性。Plugin 能够经过 Extension 暴露一些可配置的属性。这里先不讲,超纲了。

  3. execution phase
    • 根据上一步计算出的任务执行顺序去执行须要执行的 Tasks。

以上就是 Gradle 的工做过程。

Tricks

  1. 使用 Proxy

    在国内特殊的网络环境,能够经过设置 Proxy 或 Repo Mirror 的方式来提升下载依赖的 Library 的速度。

    阿里提供的镜像 maven.aliyun.com/mvn/view Gradle 使用 Java 的 Networking Properties 读取 Proxy 参数。可供设置的参数参考如下文档。

  2. 注意你的电脑中运行了多少 Gradle Daemon

    Gradle 提供了 Daemon 机制来提升构建速度,可是 Gradle Daemon 的复用是有条件的。 若是恰巧给 Gradle Daemon 设置了一个比较大的 maximum heap size, 可能在开发的过程当中,多个 Daemon 会占用过多的内存,影响电脑运行速度。除了前面给出的条件,还有两点是以前开发过程当中遇到过的:

    • 没有正确使用 Gradle 提供的 wrapper Task 去升级 Gradle 版本,致使在使用 Gradle Wrapper Script 运行任务时,判断 Gradle 版本的函数不兼容,启用多个 Daemon。这个问题的表现方式通常发现同一个版本的 Gradle 被 Gradle Wrapper 重复下载。
    • IDE 提供了 Build in JRE,致使在 IDE 中运行的 Gradle 和在 Terminal 下运行的 Gradle 虽然版本相同,可是没法复用。能够统一二者使用的 JRE。好比在 Android Studio 中打开 Project Structure,在 SDK Location Tab 下设置 JDK Location,使其和 Terminal 中使用的 Java 路径统一。

参考资料

《Gradle In Action》 -- Benjamin Muschko

Gradle 最佳实践

构建工具的进化:ant, maven, gradle

@Eric

相关文章
相关标签/搜索