内容简介: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)的方法。
2. 通过task容器TaskContainer对象的create()方法进行创建。
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 可以通过重载方法进行参数指定,也可以通过闭包的形式进行配置。
这里的group 配置的分组方便task快速定位以及后期维护,如不指定将默认分配到other组。
2. 获取Task的方式
TaskContainer为我们提供了两个方法,findByPath() 和 getByPath () 。通过查看官方文档可以知道,这两个方法都可以接收task 的名字,相对路径,绝对路径作为参数来查找对应的具体的Task。区别仅在于方法的返回值,findByPath()如果查找不到会直接放回null,而getByPath ()查找不到则会抛出UnknownTaskException异常,所以使用getByPath ()时要用try catch配合使用。注:在gradle低版本还提供过findByName() 、getByName() 方法。不过现在已经不提供这两个方法了,都通过findByPath() 和 getByPath ()来实现。
3. Task的执行
当执行一个Task的时候,其实就是执行其拥有的actions列表,这个列表保存在Task对象实例中的actions成员变量中,其类型就是一个List。Task本质上又是由一组被顺序执行的Action对象构成,Action其实是一段代码块,类似与 Java 中的方法。这里主要介绍Task创建Action的两个方法,doFirst与doLast。doFirst{} 可以使代码在Gradle 的执行阶段中Task之前执行,而doLast{}则恰恰相反,是在Task之后执行。
通过查看Task的源码可以知道,doFirst{}就是在Task存放actions的List第一位添加,保证其添加的Action在现有actions List元素的最前面;doLast{}是在actions List末尾添加,从而实现Task中actions顺序执行的目的。
-
Tips: “<<” 操作符在Gradle 的Task上是doLast方法的短标记形式,也就是说“<<” 可以替代doLast。
4. Task执行顺序
4.1 Task的依赖
dependsOn 是task 配置参数之一,主要作用就是为task 添加依赖task ,保证task 之间的执行顺序。通过下面的测试代码来理解一下:
输出结果:
我们执行了./gradlew taskC ,而在上面我们给taskC指定了依赖taskB, taskB的执行又依赖于taskA 。 所以执行结果为taskA -> taskB -> taskC。
4.2 输入输出
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
上图为tinker的工程目录,与构建相关的代码都在tinker-build 这个module里,我们主要分析的是里面的TinkerPatchPlugin.groovy文件的代码。
TinkerPatchPlugin是一个自定义插件类,主要是来实现对热修复目标工程的配置相关文件的操作。
我们主要分析的代码片段为下图,在project.afterEvaluate{} 方法中遍历获取到所有的Android变体,然后创建了一个自定义任务TinkerManifestTask ,通过当前的变体拿到AndroidManifest.xml文件的路径,并传给了TinkerManifestTask ,接着通过mustRunAfter 指定TinkerManifestTask 在构建过程中assembleRelease当中:processReleaseManifest任务之后执行,从而达到在AndroidManifest.xml追写tinker相关的配置信息。
TinkerManifestTask 中具体将tinker配置信息写入AndroidManifest.xml的核心代码。
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
实践
ndk编译 , 在Android中编译生成动态库有两种方式:
第一种是通过Android.mk脚本执行ndk-build命令来进行手动编译。 这种情况下,可以通过自定义task来实现自动化编译ndk。
这里我们自定义的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 开发相关的文件以及配置。
而生成的这些文件中需要我们关注主要文件便是CMakeLists.txt。
在配置好CMakeLists.txt,不需要单独执行编译命令,因为gradle 已经默认在build task 中预置了NDK编译相关的操作。所以只要我们正常对工程编译,便可在/build/intermediates/cmake/目录下找到生成的动态库。
Thanks!
作者简介:
焦俊楠,民生科技有限公司用户体验技术部开发工程师。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
PHP典型模块与项目实战大全
杨宇 / 清华大学出版社 / 2012-1 / 79.00元
《PHP典型模块与项目实战大全》以实战开发为原则,以PHP典型模块和项目开发为主线,通过12个高质量的PHP典型模块和6个PHP大型应用,向读者揭示了Web开发的整体结构,并详尽地介绍PHP开发与建站的技术要点。《PHP典型模块与项目实战大全》附带1张DVD,内容是作者为《PHP典型模块与项目实战大全》录制的全程多媒体语音教学视频及《PHP典型模块与项目实战大全》所涉及的源代码。《PHP典型模块与......一起来看看 《PHP典型模块与项目实战大全》 这本书的介绍吧!