gradle与agp构建api:如何编写插件

谷歌开发者 谷歌开发者     2023-03-06     436

关键词:

欢迎阅读 MAD Skills 系列之 Gradle 与 AGP 构建 API 的第二篇文章。通过上篇文章《Gradle 与 AGP 构建 API: 配置您的构建文件》您已经了解 Gradle 的基础知识以及如何配置 Android Gradle Plugin。在本文中,您将学习如何通过编写您自己的插件来扩展您的构建。如果您更喜欢通过视频了解此内容,请在此处查看: 

△ 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 了。此时查看 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,以及构建脚本中定义的产品变种。

  • 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"
   

注意,在此阶段中,我可以创建或注册新的构建类型并设置它们的属性。在阶段结束时,AGP 将会锁定 DSL 对象,这样它们就无法再被更改。如果我再次运行构建,我会看到应用的 staging 版本被构建了。

现在,假设我的一个测试没有通过,这时我想要禁用单元测试来构建一个内部版本,以找出问题所在。

为了禁用单元测试,我可以使用 beforeVariants() 回调。该回调可以让我通过 VariantBuilder 对象进行这类修改。在这里,我会检查当前变体是否是我为 staging 创建的变体。接下来,我将禁用单元测试并设置不同的 minSdk 版本。

extension.beforeVariants  variantBuilder ->
   if (variantBuilder.name == "staging") 
       variantBuilder.enableUnitTest = false
       variantBuilder.minSdk = 23
   

在此阶段后,组件列表和将要创建产物都会被确定。

本示例的完整代码如下。如需更多此类示例,请查阅 Github gradle-recipes 仓库:

https://github.com/android/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 中读取和转换产物。

您也可以通过下方二维码向我们提交反馈,或分享您喜欢的内容、发现的问题。您的反馈对我们非常重要,感谢您的支持!

推荐阅读

如页面未加载,请刷新重试

 点击屏末  | 即刻了解扩展 Android Gradle 插件更多内容


gradle与agp构建api:配置您的构建文件(代码片段)

...droidGradlepluginAPI的第一篇文章。我们将在本文中了解Android构建系统的工作方式以及Gradle的基础知识。我们将会从Gradle的构建阶段开始,讨论如何使用AGP(AndroidGradlePlugin)的配置选项自定义您的构建,并讨论如何使您的构建... 查看详情

编写 Gradle 插件时的 Gradle Api Sources 和 Doc

...-api代码的源代码和javadoc/groovydoc?背景:我正在使用Gradle构建我正在编写的Gradle插件。我使用Eclipse作为这个项 查看详情

AGP 3.5 与 Gradle 6 兼容吗?

...题描述】:我想知道我是否可以开始使用带有AndroidGradle插件(AGP)3.5的Gradle6。根据官方文档,它们不兼容:插件版本|所需的Gradle版本3.5.0+|5.4.1-5.6.4来源:https://developer.android.com/studi 查看详情

groovygradle构建工具(自动下载并配置构建环境|提供api扩展与开发工具集成|内置maven和ivy依赖管理|使用groovy编写构建脚本)(代码片段)

文章目录一、Gradle自动下载并配置构建环境二、Gradle提供API扩展与开发工具集成三、Gradle内置Maven和Ivy依赖管理四、Gradle使用Groovy编写构建脚本一、Gradle自动下载并配置构建环境GradleWrapper(包装器)作用:自动搭建Gradle构建环境,防... 查看详情

gradle构建javaweb应用:servlet依赖与tomcat插件(转)

 Gradle的官方tutorial介绍了构建JavaWeb应用的基本方法。不过在使用Servlet做上传的时候会碰到问题。这里分享下如何通过Servlet上传文件,以及如何使用Gradle来构建相应的JavaWeb工程。参考原文:HowtoBuildWebScanningApplicationwithGradleSer... 查看详情

androidgradle插件gradle构建工具简介③(gradle构建脚本编程语言|groovy语言简介|groovy语言特性)

文章目录一、Gradle构建脚本编程语言二、Groovy语言简介三、Groovy语言特性一、Gradle构建脚本编程语言Gradle构建工具的构建脚本可以使用Groovy语言或Kotlin语言进行编写,使用Groovy语言编写的构建脚本是build.gradle构建脚本;当前大量的A... 查看详情

更新到3.0后,gradle构建失败(代码片段)

...本从2.14.1更新到3.0。从那时起,每次出现此错误时,gradle构建都会失败:错误:原因:org.gradle.api.internal.tasks.DefaultTaskInputs$TaskInputUnionFileCollection无法强制转换为org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection此意外错误... 查看详情

在编写 Gradle 插件时我是不是应该影响 Kotlin

...:28:23【问题描述】:我正在编写一个插件来从现有的Gradle构建脚本中提取一些样板。现有的构建脚本主要是用Groovy编写并编译Java。为了构建我的插件,我正在使用GradleKotlinD 查看详情

gradlereceipes(agp-7.3)&agp使用指南(代码片段)

...大而导致不能使用了。(我信你个鬼)另外Gradle插件和AndroidStudio的版本之间存在着兼容要求,请参考这里我的电脑目前使用的AndroidStudio的版本是小海豚,所以AGP的要求是7.3,Gradle的要求是至少7.4:GroovyUse... 查看详情

androidgradle插件gradle依赖管理①(org.gradle.api.project配置|androidgradle插件配置与gradle配置关联)★(代码片段)

文章目录一、org.gradle.api.Project配置二、AndroidGradle插件配置与Gradle配置关联AndroidPluginDSLReference参考文档:AndroidGradle插件配置与Gradle配置关联:【AndroidGradle插件】Gradle依赖管理①(org.gradle.api.Project配置|AndroidGradle插件配置与Gradle配置... 查看详情

android笔记-自定义gradle插件(代码片段)

...么可以提取这些tasks到一个自定义插件中,使得重用构建逻辑,并且可以与团队其他人分享。插件即可以用Groovy编写,也可以使用JVM语言编写。大部分Gradle的Android插件都是Java结合Groovy编写的。一般步骤如下: 1.1 查看详情

android笔记-自定义gradle插件(代码片段)

...么可以提取这些tasks到一个自定义插件中,使得重用构建逻辑,并且可以与团队其他人分享。插件即可以用Groovy编写,也可以使用JVM语言编写。大部分Gradle的Android插件都是Java结合Groovy编写的。一般步骤如下: 1.1 查看详情

androidgradle插件与gradle的区别(代码片段)

...哪些类型?Gradle和Gradle插件的联系与区别Gradle是一种构建工具,能够简化你的编译、打包、测试过程。传统的构建工具还有Maven、Ant+Ivy、Make。这里我们所说的gradle,其实分为了两部分gradle核心和g 查看详情

androidgradle插件自定义gradle任务①(gradle面板显示任务列表|自定义任务生成与显示分组)(代码片段)

...务生成与显示分组AndroidPluginDSLReference参考文档:AndroidStudio构建配置官方文档:https://developer.android.google.cn/studio/build添加构建依赖项参考文档:https://developer.android.google.cn/studio/build/dependenciesAndroidGradle插件配置与Gradle配置关联:【Android... 查看详情

如何使用 c 插件在 gradle 中构建 emcc

】如何使用c插件在gradle中构建emcc【英文标题】:Howtobuildwithemccingradleusingcplugin【发布时间】:2019-06-2910:25:57【问题描述】:我正在尝试使用gradle编译带有emcc的项目,但到目前为止未能找到任何体面的示例来说明如何完成(或自... 查看详情

如何在mac上安装gradle

参考技术AGradle介绍Gradle是一个基于JVM的构建工具,它提供了:像Ant一样,通用灵活的构建工具可以切换的,基于约定的构建框架强大的多工程构建支持基于ApacheIvy的强大的依赖管理支持maven,Ivy仓库支持传递性依赖管理,而不需... 查看详情

如何在mac上安装gradle

参考技术AGradle介绍Gradle是一个基于JVM的构建工具,它提供了:像Ant一样,通用灵活的构建工具可以切换的,基于约定的构建框架强大的多工程构建支持基于ApacheIvy的强大的依赖管理支持maven,Ivy仓库支持传递性依赖管理,而不需... 查看详情

如何在mac上安装gradle

参考技术AGradle介绍Gradle是一个基于JVM的构建工具,它提供了:像Ant一样,通用灵活的构建工具可以切换的,基于约定的构建框架强大的多工程构建支持基于ApacheIvy的强大的依赖管理支持maven,Ivy仓库支持传递性依赖管理,而不需... 查看详情