Gradle Task原理与实践

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

内容简介: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原理与实践


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

查看所有标签

猜你喜欢:

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

PHP典型模块与项目实战大全

PHP典型模块与项目实战大全

杨宇 / 清华大学出版社 / 2012-1 / 79.00元

《PHP典型模块与项目实战大全》以实战开发为原则,以PHP典型模块和项目开发为主线,通过12个高质量的PHP典型模块和6个PHP大型应用,向读者揭示了Web开发的整体结构,并详尽地介绍PHP开发与建站的技术要点。《PHP典型模块与项目实战大全》附带1张DVD,内容是作者为《PHP典型模块与项目实战大全》录制的全程多媒体语音教学视频及《PHP典型模块与项目实战大全》所涉及的源代码。《PHP典型模块与......一起来看看 《PHP典型模块与项目实战大全》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

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

URL 编码/解码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具