Gradle Task原理与实践

栏目: IOS · Android · 发布时间: 6年前

内容简介:Task

前言 Gradle 是一款基于Apache Ant 和Apache Maven概念的功能强大的项目自动化构建工具,它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,抛弃了基于XML的各种繁琐配置。Gradle 对多工程的构建支持很出色,还可以根据特定需求开发自定义插件来解决特定问题,因此掌握Gradle 核心技术可以提高我们的开发效率,本文是对Gradle 核心之一Task的相关属性、执行流程、自定义任务等进行介绍。

Task

我们的所有Gradle的构建工作都是由Task组合完成的,它可以帮助我们处理很多工作。 每次构建(build)至少由一个project构成,一个project由一个至多个task构成。每个task代表了构建过程当中的一个原子性操作,比如编译、打包、发布等等这些操作。在Gradle环境下可以通过命令./gradlew tasks 查看当前工程所有的task。

1.     Task的创建和配置

Task创建: Gradle 有多种创建task的方式,这里只介绍常见的两种方式。

1. 直接使用task函数创建,其实是调用Project对象中的task(String name)的方法。  

Gradle Task原理与实践

2. 通过task容器TaskContainer对象的create()方法进行创建。

Gradle Task原理与实践

Task 配置: 上面两种创建方式其实都有对应的重载方法,可以传入具体的配置参数来进行Task初始化配置。

配置项

描述

默认值

type

基于一个存在的Task来创建

DefaultTask

overwrite

是否替换存在的task,配合type使用

false

dependsOn

用于配置task的依赖

[ ]

action

添加到task中的一个Action或者一个闭包

null

description

用于配置task的描述

null

group

用于配置task的分组

null

name

用于配置task的名称

null

T ask 可以通过重载方法进行参数指定,也可以通过闭包的形式进行配置。

Gradle Task原理与实践

这里的group 配置的分组方便task快速定位以及后期维护,如不指定将默认分配到other组。

2.      获取Task的方式

TaskContainer为我们提供了两个方法,findByPath() 和 getByPath () 。通过查看官方文档可以知道,这两个方法都可以接收task 的名字,相对路径,绝对路径作为参数来查找对应的具体的Task。区别仅在于方法的返回值,findByPath()如果查找不到会直接放回null,而getByPath ()查找不到则会抛出UnknownTaskException异常,所以使用getByPath ()时要用try catch配合使用。注:在gradle低版本还提供过findByName() 、getByName() 方法。不过现在已经不提供这两个方法了,都通过findByPath() 和 getByPath ()来实现。

Gradle Task原理与实践

3.   Task的执行

当执行一个Task的时候,其实就是执行其拥有的actions列表,这个列表保存在Task对象实例中的actions成员变量中,其类型就是一个List。Task本质上又是由一组被顺序执行的Action对象构成,Action其实是一段代码块,类似与 Java 中的方法。这里主要介绍Task创建Action的两个方法,doFirst与doLast。doFirst{} 可以使代码在Gradle 的执行阶段中Task之前执行,而doLast{}则恰恰相反,是在Task之后执行。

Gradle Task原理与实践

通过查看Task的源码可以知道,doFirst{}就是在Task存放actions的List第一位添加,保证其添加的Action在现有actions List元素的最前面;doLast{}是在actions List末尾添加,从而实现Task中actions顺序执行的目的。

  • Tips: “<<” 操作符在Gradle 的Task上是doLast方法的短标记形式,也就是说“<<” 可以替代doLast。

4.   Task执行顺序

Gradle Task原理与实践

4.1  Task的依赖

dependsOn 是task 配置参数之一,主要作用就是为task 添加依赖task ,保证task 之间的执行顺序。通过下面的测试代码来理解一下:

Gradle Task原理与实践

输出结果:

Gradle Task原理与实践

我们执行了./gradlew taskC ,而在上面我们给taskC指定了依赖taskB, taskB的执行又依赖于taskA 。 所以执行结果为taskA ->  taskB  -> taskC。

4.2  输入输出

Gradle Task原理与实践

TaskInputs:

Task的输入类,参数可以接收为任意对象以及文件、文件夹。  

TaskOutputs:

TaskOutputs files ( );

TaskOutputs file ( );

TaskOutputs dir ( );

Task的输出类,只接收文件类型。

4.3  API指定执行顺序

Task的shouldRunAfter 方法和mustRunAfter方法可以控制一个Task应该或者一定在某个Task之后执行。 通过这种方式可以在某些情况下控制任务的执行顺序,而不是通过强依赖的方式。

taskB.shouldRunAfter(taskA)表示taskB应该在taskA执行之后执行,这里并不是强制的,所以有可能任务顺序并不会按预设的执行。

taskB.mustRunAfter(taskA)表示taskB必须在taskA执行之后执行,执行任务的顺序是确认的。

  • 注: 使用mustRunAfter方法,如果出现Task之间依赖语法矛盾,依赖关系形成闭环,编译器会报错。而shouldRunAfter 方法会自动打破闭环,不会报错。

5.    挂接自定义Task到构建过程中

在此之前先介绍下Gradle 的生命周期:

A. 初始化阶段(Initialization)

初始化阶段gradle会去解析项目根工程中setting.gradle中的include信息,确定哪些工程加入构建。  

B. 配置阶段(Configuration)

配置阶段将解析所有工程的build.gradle脚本,配置project对象,创建、配置task等相关信息。

C. 执行阶段(Execution)

根据具体的gradle命令,执行对应相关的task以及其依赖的task。

而实际项目中我们将要挂接的正是gradle的执行阶段,因为只有在配置阶段完成,执行阶段gradle才会去执行系统默认task 以及自定义task 。project为我们提供了这样的方法project.afterEvaluate{}。

project.afterEvaluate{} 在配置完成后,可以保证获取到所有的task,包括系统默认执行的task,这样就可以将我们自定义的task 通过顺序执行指定,挂接到构建过程中。实际开发中,一般我们都会对系统的assembleRelease任务进行挂载,先来了解一下assembleRelease的内部执行顺序:

:preBuild

:preReleaseBuild

:checkReleaseManifest

:prepareReleaseDependencies

:compileReleaseAidl

:compileReleaseNdk

:compileReleaseRenderscript

:generateReleaseBuildConfig

:generateReleaseResValues

:generateReleaseResources

:mergeReleaseResources

:processReleaseManifest

:processReleaseResources

:generateReleaseSources

:incrementalReleaseJavaCompilationSafeguard

:javaPreCompileRelease

:compileReleaseJavaWithJavac

:extractReleaseAnnotations

:mergeReleaseShaders

:compileReleaseShaders

:generateReleaseAssets

:mergeReleaseAssets

:mergeReleaseProguardFiles

:packageReleaseRenderscript

:packageReleaseResources

:processReleaseJavaRes

:transformResourcesWithMergeJavaResForRelease

:transformClassesAndResourcesWithSyncLibJarsForRelease

:mergeReleaseJniLibFolders

:transformNativeLibsWithMergeJniLibsForRelease

:transformNativeLibsWithSyncJniLibsForRelease

:bundleRelease

:compileReleaseSources

:assembleRelease

  • 注: 上面代码基本上列举了Gradle构建Android项目时执行assembleRelease的所有Task,Lint、Test等非必需Task除外。

下面我们将结合腾讯开源的热修复方案Tinker的源码来分析将自定义task挂接构建过程的原理。

Tinker GitHub链接 https://github.com/Tencent/tinker

Gradle Task原理与实践

上图为tinker的工程目录,与构建相关的代码都在tinker-build 这个module里,我们主要分析的是里面的TinkerPatchPlugin.groovy文件的代码。

TinkerPatchPlugin是一个自定义插件类,主要是来实现对热修复目标工程的配置相关文件的操作。

Gradle Task原理与实践

我们主要分析的代码片段为下图,在project.afterEvaluate{} 方法中遍历获取到所有的Android变体,然后创建了一个自定义任务TinkerManifestTask ,通过当前的变体拿到AndroidManifest.xml文件的路径,并传给了TinkerManifestTask ,接着通过mustRunAfter 指定TinkerManifestTask 在构建过程中assembleRelease当中:processReleaseManifest任务之后执行,从而达到在AndroidManifest.xml追写tinker相关的配置信息。

Gradle Task原理与实践

TinkerManifestTask 中具体将tinker配置信息写入AndroidManifest.xml的核心代码。  

Gradle Task原理与实践

6.   Task的启用与禁用  

Task中有个enabled属性,用于启用和禁用任务,默认是true,表示启用,设置为false则禁止该任务,在执行阶段该任务会被跳过。在实际项目中是很使用的一个技巧,如提升build编译速度,禁止一些测试相关的Task,从而缩短执行时间。

7.   Task的onlyIf断言

断言就是一个条件表达式。Task有一个onlyIf方法,它接受一个闭包作为参数,如果该闭包返回为true则该任务执行,否则跳过。在实际项目中有很多用途,比如控制程序在哪些情况下打包,什么时候执行单元测试等。

8.    默认Task

Gradle 为我们提供了很多默认的task来进行使用,下面是Gradle 官网文档上对copy Task 的使用提供的例子。除了copy,还有delete、upload、zip等许多使用的task。更多具体用法可以去阅读Gradle 的官方文档: https://docs.gradle.org/current/dsl/org.gradle.api.Task.html

Gradle Task原理与实践

实践

ndk编译 在Android中编译生成动态库有两种方式:

第一种是通过Android.mk脚本执行ndk-build命令来进行手动编译。 这种情况下,可以通过自定义task来实现自动化编译ndk。

Gradle Task原理与实践  

这里我们自定义的task要指定type 为Exec类型,Exec task 可以通过commandLine方法执行windows、 Linux 环境下的命令,通过预先设置好ndk 编译的命令,通过task自动执行来达到目的。(上图为windows 环境下的执行代码)

第二种通过cmake 进行编译。

在android studio 2.2及以上,构建原生库的默认 工具 是CMake。CMake是一个跨平台的构建工具,可以用简单的语句来描述所有平台的安装(编译过程)。能够输出各种各样的makefile或者project文件。Cmake 并不直接建构出最终的软件,而是产生其他工具的脚本(如Makefile ),然后再依这个工具的构建方式使用。它可以根据不同平台、不同的编译器,生成相应的Makefile或者vcproj项目。从而达到跨平台的目的。Android Studio利用CMake生成的是ninja,ninja是一个小型的关注速度的构建系统。我们不需要关心ninja的脚本,知道怎么配置cmake就可以了。从而可以看出cmake其实是一个跨平台的支持产出各种不同的构建脚本的一个工具。

现在的ndk开发中基本上都是用CMake,而之前通过Android.mk脚本方式基本已经废弃,这里简单介绍下CMake相关的配置。在使用Android Studio 创建工程时,选择Native C++ ,AS便会自动为我们生成ndk 开发相关的文件以及配置。

Gradle Task原理与实践

而生成的这些文件中需要我们关注主要文件便是CMakeLists.txt。

Gradle Task原理与实践

在配置好CMakeLists.txt,不需要单独执行编译命令,因为gradle 已经默认在build task 中预置了NDK编译相关的操作。所以只要我们正常对工程编译,便可在/build/intermediates/cmake/目录下找到生成的动态库。

Gradle Task原理与实践

Thanks!

作者简介:

焦俊楠,民生科技有限公司用户体验技术部开发工程师。 

Gradle Task原理与实践


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Lean Startup

The Lean Startup

Eric Ries / Crown Business / 2011-9-13 / USD 26.00

更多中文介绍:http://huing.com Most startups fail. But many of those failures are preventable. The Lean Startup is a new approach being adopted across the globe, chan ging the way companies are built and ......一起来看看 《The Lean Startup》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

URL 编码/解码
URL 编码/解码

URL 编码/解码