乐趣区

关于android:Gradle-与-AGP-构建-API-如何编写插件

欢送浏览 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 中读取和转换产物。

欢迎您 点击这里 向咱们提交反馈,或分享您喜爱的内容、发现的问题。您的反馈对咱们十分重要,感谢您的反对!

退出移动版