Gradle 与 AGP 构建 API: 如何编写插件
△ Gradle 与 AGP 构建 API: 如何编写插件
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 已经列在 Android Studio 的 Gradle 窗格中了
tasks.register("hello"){
doLast {
println("Hello " + project.parent?.name)
}
}
当我再次运行 Task 时,我可以看到 hello 信息是在执行阶段打印的。
在 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!")
}
}
class CustomPlugin: Plugin<Project> {
override fun apply(project: Project) {
project.tasks.register<HelloTask>("hello")
}
}
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 {
...
}
Variant
https://developer.android.google.cn/studio/build/build-variants
在您的构建文件中,使用声明式 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"
}
}
extension.beforeVariants { variantBuilder ->
if (variantBuilder.name == "staging") {
variantBuilder.enableUnitTest = false
variantBuilder.minSdk = 23
}
}
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
}
}
}
}
总结
推荐阅读