共计 4760 个字符,预计需要花费 12 分钟才能阅读完成。
欢送浏览 MAD Skills 系列 之 Gradle 与 AGP 构建 API 的第二篇文章。通过上篇文章《Gradle 与 AGP 构建 API: 配置您的构建文件》您曾经理解 Gradle 的基础知识以及如何配置 Android Gradle Plugin。在本文中,您将学习如何通过编写您本人的插件来扩大您的构建。如果您更喜爱通过视频理解此内容,请在 此处 查看。
Android Gradle Plugin 从 7.0 版开始提供稳固的扩大点,用于操作变体配置和生成的构建产物。该 API 的一些局部是最近才实现的,因而我将会在本文中应用 7.1 版 AGP (撰写本文时尚处于 Beta 版)。
Gradle Task
我会从一个全新的我的项目开始。如果您想要同步学习,能够通过抉择根底 Activity 模板来创立一个新我的项目。
让咱们从创立 Task 并打印输出开始——没错,就是 hello world。为此,我会在应用层的 build.gradle.kts 文件注册一个新的 Task,并将其命名为 “hello”。
tasks.register("hello"){}
当初 Task 曾经准备就绪,咱们能够打印出 “hello” 并加上项目名称。留神以后 build.gradle.kts
文件属于利用模块,所以 project.name
将会是以后模块的名字 “app”。而如果我是用 project.parent?.name
,就会返回我的项目的名称。
tasks.register("hello"){println("Hello" + project.parent?.name)
}
是时候运行该 Task 了。此时查看 Task 列表,能够看到我的 Task 曾经位列其中。
△ 新的 Task 曾经列在 Android Studio 的 Gradle 窗格中了
我能够双击 hello Task 或通过终端执行此 Task,并在构建输入中察看它所打印的 hello 信息。
△ Task 在构建输入中打印的 hello 信息
在查看日志时,我能够看到此信息是在配置阶段打印的。配置阶段实际上与执行 Task 的性能 (例如本例中的打印 Hello World) 无关。配置阶段是进行 Task 配置以作用于其执行的阶段。您能够在此阶段确定 Task 的输出、参数,以及输入的地位。
无论申请运行哪个 Task,配置阶段都会执行。在配置阶段执行耗时操作会导致较长的配置工夫。
Task 的执行该当只在执行阶段产生,所以咱们须要将打印调用挪动至执行阶段。我能够通过增加 doFirst() 或 doLast() 函数来达到这一目标,二者别离能够在执行阶段的开始和完结时打印 hello 音讯。
tasks.register("hello"){
doLast {println("Hello" + project.parent?.name)
}
}
当我再次运行 Task 时,我能够看到 hello 信息是在执行阶段打印的。
△ 当初 Task 会在执行阶段打印 hello 信息
我的自定义 Task 目前位于 build.gradle.kts
文件中。增加自定义 Task 到 build.gradle
文件是创立自定义构建脚本的不便法门。不过,在我的插件代码变得愈发简单时,这种形式不利于进行扩大。咱们倡议将自定义 Task 和插件实现搁置于 buildSrc
文件夹。
在 buildSrc 中实现插件
在编写更多代码前,让咱们将 hello Task 挪动至 buildSrc
。我会创立一个新的文件夹,并将其命名为 buildSrc
。接下来,我为插件我的项目创立了一个 build.gradle.kts
文件,这样 Gradle 就会主动将此文件夹增加至构建。
这是我的项目根文件夹中的顶层目录。留神,我并不需要在我的我的项目中将其增加为模块。Gradle 会主动编译目录中的代码,并将其退出到您构建脚本的 classpath 中。
接下来,我创立了一个新的 src 文件夹与一个名为 HelloTask
的类。我将新的类改为 abstract
类,并使其继承 DefaultTask
。随后,我会增加一个名为 taskAction
的函数、应用 @TaskAction
注解此函数,并将我自定义的 Task 代码迁徙至此函数中。
abstract class HelloTask: DefaultTask() {
@TaskAction
fun taskAction() {println("Hello \"${project.parent?.name}\"from task!")
}
}
当初,我的 Task 曾经就绪。我会创立一个新的插件类,这须要实现 Plugin
类型并笼罩 apply()
函数。Gradle 会调用此函数并传入 Project
对象。为了注册 HelloTask
,我须要在 project.tasks
上调用 register()
,并为这个新的 Task 命名。
class CustomPlugin: Plugin<Project> {override fun apply(project: Project) {project.tasks.register<HelloTask>("hello")
}
}
此时,我也能够将我的 Task 申明为依赖其余 Task。
class CustomPlugin: Plugin<Project> {override fun apply(project: Project) {project.tasks.register<HelloTask>("hello"){dependsOn("build")
}
}
}
上面让咱们利用新的插件。留神,如果我的我的项目含有多个模块,我也能够通过将此插件退出其余 build.gradle 文件来复用它。
plugins {id ("com.android.application")
id ("org.jetbrains.kotlin.android")
}
apply<CustomPlugin>()
android {...}
当初,我会运行 hello Task,并像之前一样察看插件的运行。
./gradlew hello
到目前为止,我曾经将我的 Task 移至 buildSrc
,让咱们更进一步,摸索新的 Android Gradle Plugin API。AGP 为其构建产物时的生命周期提供了扩大点。
在开始学习 Variant API 前,让咱们先理解什么是 Variant。变体 (variant) 是您利用能够构建的不同版本。假如除了性能残缺的利用,您还心愿构建一个演示版的利用或用于调试的外部版本。您还能够针对不同的指标 API 或设施类型。变体由多个构建类型组合而成,例如 debug 与 release,以及构建脚本中定义的产品变种。
在您的构建文件中,应用申明式 DSL 增加构建类型是齐全没有问题的。不过,在代码中以这种形式让您的插件影响构建是不可能的,或者说难以使用申明式语法进行表白。
AGP 通过解析构建脚本及 android
块中设置的属性来启动构建。新的 Variant API 回调让我能够从 androidComponents
扩大中增加 finalizeDSL()
回调。在此回调中,我能够在 DSL 对象利用于 Variant 创立前对它们进行批改。我将创立一个新的构建类型并且设置它的属性。
val extension = project.extensions.getByName("androidComponents") as ApplicationAndroidComponentsExtension
extension.finalizeDsl { ext->
ext.buildTypes.create("staging").let { buildType ->
buildType.initWith(ext.buildTypes.getByName("debug"))
buildType.manifestPlaceholders["hostName"] = "example.com"
buildType.applicationIdSuffix = ".debugStaging"
}
}
留神,在此阶段中,我能够创立或注册新的构建类型并设置它们的属性。在阶段完结时,AGP 将会锁定 DSL 对象,这样它们就无奈再被更改。如果我再次运行构建,我会看到利用的 staging 版本被构建了。
当初,假如我的一个测试没有通过,这时我想要禁用单元测试来构建一个外部版本,以找出问题所在。
为了禁用单元测试,我能够应用 beforeVariants()
回调。该回调能够让我通过 VariantBuilder
对象进行这类批改。在这里,我会查看以后变体是否是我为 staging
创立的变体。接下来,我将禁用单元测试并设置不同的 minSdk
版本。
extension.beforeVariants { variantBuilder ->
if (variantBuilder.name == "staging") {
variantBuilder.enableUnitTest = false
variantBuilder.minSdk = 23
}
}
在此阶段后,组件列表和将要创立产物都会被确定。
本示例的残缺代码如下。如需更多此类示例,请查阅 Github gradle-recipes 仓库:
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
class CustomPlugin: Plugin<Project> {override fun apply(project: Project) {project.tasks.register("hello"){ task->
task.doLast {println("Hello" + project.parent?.name)
}
}
val extension = project.extensions.getByName("androidComponents") as ApplicationAndroidComponentsExtension
extension.beforeVariants { variantBuilder ->
if (variantBuilder.name == "staging") {
variantBuilder.enableUnitTest = false
variantBuilder.minSdk = 23
}
}
extension.finalizeDsl { ext->
ext.buildTypes.create("staging").let { buildType ->
buildType.initWith(ext.buildTypes.getByName("debug"))
buildType.manifestPlaceholders["hostName"] = "internal.example.com"
buildType.applicationIdSuffix = ".debugStaging"
// 在前面解释 beforeVariants 时增加了本行代码。buildType.isDebuggable = true
}
}
}
}
总结
编写您本人的插件,您能够扩大 Android Gradle Plugin 并依据您的我的项目需要自定义您的构建!
在本文中,您曾经理解了如何应用新的 Variant API 来在 AndroidComponentsExtension
中注册回调、应用 DSL 对象初始化 Variant、影响已被创立的 Variant,以及在 beforeVariants()
中它们的属性。
在下一篇文章中,咱们将进一步介绍 Artifacts API,并向您展现如何从您的自定义 Task 中读取和转换产物。
欢迎您 点击这里 向咱们提交反馈,或分享您喜爱的内容、发现的问题。您的反馈对咱们十分重要,感谢您的反对!