在开发Android时,我们希望大部分tasks都能涉及到Android插件。通过hook到构建进程来增加任务的行为是可行的。在上一篇中我们看到如何为自定义任务添加一个依赖,以在常规的构建过程中包含新的任务。
Hook到Android插件的方式之一时操控variants。这种做法相当简单,你只需用下面的片段遍历应用的所有构建variant即可:
android.applicationVariants.all{ variant ->
//Do something
}
你可以使用applicationVariants对象来得到构建variant的集合。一旦你有一个构建variant的引用,你就可以访问和操作它的属性,比如名称,说明等。如果你想对Android依赖库使用相同的逻辑,请使用libraryVariants来代替applicationVariants。
注意:我们通过all()来遍历构建variant,而不是之前提到的each()。这是因为each()会在构建variant被Android插件创建之前的评测阶段被触发。另外,all()方法会在每次添加新项目到集合时被触发。
该Hook可以用来修改保存之前的APK的名称,并未文件名添加版本号。这使得维护APK的归档文件变得更加容易,因为无需手动编辑文件名。
自动重命名APK
一个常用的案例是操纵构建过程来重命名APKs,在他们被打包之后,添加版本号。你可以通过遍历应用的构建variant,来改变他们的输出属性outputFile,代码如下:
android.applicationVariants.all{variant ->
variant.outputs.each{ output ->
def file = output.outputFile
output.outputFile = new File(file.parent,file.name.replace(".apk","-${variant.versionName}.apk"))
}
}
每个构建variant都有输出集合。Android应用的唯一输出就是一个APK。每个输出对象都有一个名为outputFile类型的属性。一旦你知道其输出路径,就可以操纵他。在上面的例子中,我们添加了variant的版本号到文件名中。
动态创建新的任务
由于Gradle的工作方式和任务的构造方式,我们可以轻松的基于Android构建variant,在配置阶段创建我们自己的任务。为了证明这一强大的概念,我们将创建一个不是用于install的任务,并且使其能在Android应用的任何构建variant上运行。install任务是Android插件的一部分,但如果通过命令行界面使用installDebug任务来安装App,那么在安装完成时,你仍然需要手动启动它,我们将在下面创建新的任务,来省掉最后一步。
从我们之前使用的hook到applicationVariants属性开始:
android.applicationVariants.all{variant ->
if (variant.install){
tasks.create(name:"run${variant.name.capitalize()}",
dependsOn:variant.install){
description "Installs the ${variant.description} and runs the main launcher activity."
}
}
}
对于每个variant,我们都会检查其是否是一个有效的install任务。该步不能省略,以为我们正在创建一个新的任务依赖install任务。一旦验证了install task的存在,我们就可以创建新的任务,并既无variant的名称为其命名。我们也让新的任务依赖于variant.install,其将在我们的任务执行之前触发instal任务。首先在tasks.create()closure内添加一份说明,此说明将在你执行gradlew任务时显示。除添加说明外,我们还需要添加实际的任务动作。在本例中,我们希望启动应用。你可以通过Android Debug Tool,在一个连接的设备或者模拟器上启动一个应用。
Gradle有一个叫做exec()的方法,器可以执行一个命令行进程。为了shiexec()能够工作,我们需要提供一个存在的可执行的PATH环境变量。我们需要通过args属性传递所有参数,因为其需要一些列字符串,下面是代码块:
doFirst{
exec{
executable = 'adb'
args = ['shell','am','start','-n',"${variant.applicationId}/.MainActivity"]
}
}
使用构建variant的applicationID,可获得完整的包名,包括后缀如果有的话。在这种情况下,后缀依然有一个问题。即便我们添加了后缀,activity的类路径依然相同。
创建自己的插件
如果你想在多个项目中复用一系列Gradle tasks,那么提取这些tasks到一个自定义插件中将非常有用。这样就可以重用自己的构建逻辑,并与他人分享。
插件既可以使用Groovy编写,也可以使用其他JVM语言编写,例如Java和Scala。实际上,Gradle的Android插件大部分都是由Java结合Groovy编写的。
创建一个简单的插件
你可以在build.gradle文件中创建一个插件,来提取已存储在你构建配置文件中的构建逻辑。这也是入门自定义插件最简单的方式。
要像创建一个插件,首先需要创建一个实现Plugin接口的类。我们将利用编写的代码动态创建run任务。
class RunPugin implements Plugin<Project> {
@Override
void apply(Project project) {
project.android.applicationVariants.all{variant ->
if (variant.install){
project.tasks.create(name:"run${variant.name.capitalize()}",dependsOn: variant.install){
}
}
}
}
}
Plugin接口定义了一个apply()方法。Gradle再插件被构建文件使用时,调用此方法。Project将作为参数被传递,这样插件就可以配置项目或使用它的方法和属性了。再前面的例子中,我们无法从Android插件中直接调用属性,而是需要先访问project对象。注意,在我们的自定义插件被应用之前,Android插件需要被应用到项目中来。否则,project.android将导致异常。
这个任务的代码和之前的相同,只有一个方法调用不同:不是调用exec(),这里需要调用project.exec().
为了确保该插件会应用到我们的构建配置,需要再build.gradle中加入这一行:
apply plugin:RunPlugin
分发插件
为了分发插件,并与他人分享,你需要把它移到一个独立的模块(或项目中)。一个独立的插件有其自己的构建文件来配置依赖和分发方式。这个模块会产生一个包含插件类和属性的JAR文件。你可以再多个模块和项目中使用此JAR文件,并且可以与他人分享。
与任一Gradle项目相同,我们可以创建一个build.gradle文件来配置构建:
apply plugin:‘groovy’
dependencies{
compile gradleApi()
compilelocalGroovy()
}
由于是在Groovy中编写插件,因此我们要应用这个Groovy插件。Groovy插件继承自Java插件,其允许你构建和打包Groovy类。Groovy和普通的Java都支持它,所以如果你喜欢,你可以任意混合。你甚至可以走的更远,例如使用Groovy继承一个Java类,或其他方式。这使其易于上手,即使你对全部使用Groovy没有信心。
我们的构建配置文件包含两个依赖:gradleApi()和localGroovy()。从自定义插件中访问Gradle命名空间需要用Gradle API,localGroovy()是Groovy SDK的一个分发包,来自于Gradle安装包。Gradle默认情况下提供了这些依赖。如果Gradle没有提供这些现成的依赖,那么我们必须手动下载和引用它们。