最近需要做了一个功能,需要将Android工程中的一个lib库中所依赖的aar、jar等直接都打包,在最后的生成产物中-aar包含以上所有的依赖库;我们在开发Android过程中,也会遇到对编译过程进行干扰;当前Android工程的开发都是基于Gradle进行构建的,等等这些都要求对于Gradle我们需要进行一个比较全面的了解。
什么是Gradle
Gradle是一个开源的构建脚本工具,能够协助大家更好的提高开发效率;Gradle的构建脚本都是使用Groovy或者Kotlin或者Java编写的;Gradle提供了大量的API,我们可以根据这些API编写出自己的工具(Plugin-插件工具),利用自己编写的工具实现自己的需求,例如:复制文件、将特定文件目录的java源代码打包成jar包等。Gradle构建工具在最后还是启动了JVM,帮助自己干事情。
Gradle初体验
对于Gradle的安装可以参考官方文档,安装完成以后我们可以使用 gradle init(对于IDE创建工程,这个命令有IDE自己完成)命令初始化一个gradle构建的工程,查看当前的文件目录如下:
├── build.gradle
├── settings.gradle
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
└── gradlew.bat
这个就是使用gradle构建一个工程必须的一些文件,这些文件的和我们创建的Android工程是不是有点类似呢。接下来对这些文件的用途做一个简单的介绍:
build.gradle:主要是为执行的一些task配置一些参数,或者在这里添加自己的参数等;对应与Android构建工程中,在该文件中配置了编译时使用的sdk版本、编译工具版本等参数;这里配置中最终要的一点就是需要使用哪些插件,使用了什么插件就是使用了哪些task;
setting.gradle:配置一下当前工程目录中,哪些子工程需要参与构建;
gralde:该目录中包含了gradle-wrapper.jar和gradle-wrapper.properties两个文件,其中jar包就是一个可执行的gradle,gradle-wrapper.properties主要是配置一些与这个可执行jar包相关的参数:
#Sun Mar 11 21:54:32 CST 2018
#分配的最基本的地址路径,主要是用于存储gradle.jar
distributionBase=GRADLE_USER_HOME
#gradle.jar存储的相对路径,与前面的基本路径组合成了绝对路径
distributionPath=wrapper/dists
#下载gradle.jar的zip类型文件存储的基本路径
zipStoreBase=GRADLE_USER_HOME
#zip文件存储的相对路径,与前面的基本路径组合为绝对路径
zipStorePath=wrapper/dists
#需要下载的对应版本gradl压缩包
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-bin.zip
gradlew与gradlew.bat:这两个文件都是可执行的脚本,功能都是相同的,只是对应不同系统下的执行脚本,分别对应Unix base和Window系统。执行该脚本的功能与直接在命令行执行gradle功能是相同的,那这个脚本存在的意义是什么呢,主要的意义在于可以执行特定版本的gradle,也就是上面gradle文件下的jar包,我们需要修改gradle-wrapper.properties下对应的属性,并重新执行gradlew脚本就会重新下载。
Gradle与插件
对于插件比较直观的体验就是安装了插件我可以使用特定的功能,例如在浏览器上安装了Octotree,我们在浏览Github上的开源工程的时候,可以在左侧直接查看工程目录。对于Gradle构建的工程,如果我们需要使用某些特定功能的构建工具,也是以插件的形式完成的。
插件的主要功能就是利用Gradle 定义的API实现自己的功能,前面已经说过Gradle开放了大量的API,这些API主要是定义了基本的一些功能:copy文件、文件打包为jar包等;对于Android开发来说比较常见的插件有下面两个:
//构建Android App的插件
apply plugin: 'com.android.application'
//构建Android Lib的插件
apply plugin: 'com.android.library'
其实上面的这些插件都是Android官方为广大开发者提供的,对于App构建的过程有了解过的人肯定比较清晰的知道,里面包括了R.java文件的生成、dex文件的生成、打包等;对于正常情况下如果不使用自动打包构建脚本,我们肯定需要使用Android提供的最原生的工具aapt、dx等按部就班的生成对应的文件和转换,最后打包压缩为APK文件。但是使用这些构建脚本可以大大提高我们的开发效率、发布效率、构建效率等。
自定义Gradle插件-自己动手
在构建自己的Gradle之前需要了解两个基本的API接口,方便更好的理解自己编写的Gradle插件。
基本知识点
Project
在前面初步体验Gradle的时候,简单的创建了一个Gradle构建工程,对于一个工程就对应与Gradle中的一个Project对象,Project对象里面定义了大量与该工程对应的变量、操作方法等。我们在使用 gradle 工具构建工程的时候会读取目录文件中的 build.gradle 文件,该文件配置了Project相关的属性;
Task
对于一个工程来说,当需要构建的时候肯定需要一步一步的完成某些步骤,最后产生自己需要的产物;对于Gradle构建来说,把这些一个一个的步骤对应了一个Task对象;还是以Android打包过程来进行分析,如果我们需要将当前的java原文件编译成class文件,这里就需要使用cmpileJava这个Task;对于Android打包的插件也就是定义了大量的Task,然后将这些Task添加到Project中,并定义好它们的顺序,在构建脚本启动的时候按照先前定义好的顺序,逐个执行这些Task,最终就会得到我们自己想要的产物—APK文件。
开始自定义
下面就开始来编写自己的插件,并执行运行该插件。首先需要创建一个Gradle构建工程,直接使用 gradle init 命令创建了我们在第一部分的工程目录:
├── build.gradle
├── settings.gradle
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
└── gradlew.bat
然后使用如下命令创建目录:
mkdir -p buildSrc/src/main/java/com/github/songnick/example
然后创建文件:buildSrc/src/main/java/com/github/songnick/example/ExamplePlugin.java
package com.github.songnick.example;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
public class ExamplePlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
project.getTasks().create("hello", HelloTask.class, (task) -> {
task.setMessage("Hello");
task.setRecipient("World");
});
}
}
这里是实现了插件接口,并重写了apply方法,在该方法中使用project对象提供的接口定义了一个Task—HelloTask,这里的project对象就是上面的部分介绍到的,可以通过这个project对象获取工程构建的文件目录、依赖的库等信息。对于HelloTask类的具体定义如下:
buildSrc/src/main/java/com/github/songnick/example/HelloTask.java
package com.github.songnick.example;
import org.gradle.api.DefaultTask;
import org.gradle.api.tasks.TaskAction;
public class HelloTask extends DefaultTask {
private String message;
private String recipient;
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public String getRecipient() { return recipient; }
public void setRecipient(String recipient) { this.recipient = recipient; }
//具体的任务执行注解
@TaskAction
void sayGreeting() {
System.out.printf("%s, %s!\n", getMessage(), getRecipient());
}
}
这里的@TaskAction注解表示最后调用这个任务的执行方法;
当我们创建和编写完成上面的文件以后,当前的文件目录结构就变为了:
├── build.gradle
├── buildSrc
│ └── src
│ └── main
│ └── java
│ └── com
│ └── github
│ └── songnick
│ └── example
│ ├── ExamplePlugin.java
│ └── HelloTask.java
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
多出了buildSrc文件目录及里面的文件,这个时候直接执行 gradle命令或者./gradlew 这个时候就会编译buildSrc文件下的java文件,并生成了编译过后相关的产物,可以通过buildSrc/build/文件目录去查看对应的产物,其中包括class文件、jar包等。这里有可能比较疑惑,为什么执行这个命令会编译buildSrc文件下对应的文件,这个是gradle工具在执行构建的时候默认会把当前文件目录下的buildSrc文件作为自己的默认编译目录。现在我们编译完成了,怎么验证我们编写的插件是否能够正常使用呢,用编辑器打开当前目录中的build.gradle文件,并添加如下字段:
apply plugin: com.github.songnick.example.ExamplePlugin
然后执行 ./gradlew tasks 结果中会看到:
Other tasks-----------hello
这里就是我们通过自定义的插件添加的Task-名字叫hello;我们可以直接执行该task:./gradlew hello 查看结果。
上面是通过自己在命令行纯手工实现了一个自定义的插件以及Task,当然也可以通过其他IDE创建插件工程,并实现发布等,这些对应的教程也比较多,这里就不详细介绍。主要是希望通过上面简单的创建过程大家可以直接的感受一下自定义插件。
构建Gradle插件-编程语言
上面自定义插件的部分中,我们使用了Java语言编写了相关接口的实现、类的继承等;其实对于插件的编写Gradle构建工具还支持Groovy,对于语言来说我们不需要纠结太多,每个语言都有其特点;那么Gradle为啥要支持除了Java语言的Groovy语言呢,我个人的理解其实很简单,可以从Android工程使用Gradle构建看出,我们需要对工程做一些配置,这些配置都是放在 build.gradle 中,我们发现在配置这些参数的时候就是使用了Groovy语言的特性。对于Groovy语言是DSL(领域专用语言),这些语言的设计初衷是给非程序员使用,所以设计的时候就会更加的容易让人理解,使用起来更方便;对于Grooy语言的学习大家可以自行学习,这里就不过多的介绍了。
后记
简单的把Gradle的基础的基本概念和自己的理解阐述 了一下,更多的学习资料还是直接访问官网吧,对于插件的编写以及Groovy的学习可以直接学习Android构建插件的源码吧。