开发者关系工程师
随着 Android 应用规模不断扩大,AI 编码助手变得不可或缺,使用可执行代码配置 build 可能会导致同步时间变慢、逻辑脆弱和复杂性增加,从而造成瓶颈。
Lightbuild 闪亮登场:这是 Google 为构建引擎打造的声明式、配置驱动型界面,专为现代 Android 开发而设计。
在本讲座中,我们将探索 Lightbuild 管理 build 的新方法,以及如何使用它来构建演示应用。了解如何将 build 配置与执行解耦,从而在 Android Studio 中实现更快的项目打开速度和闪电般的同步速度,同时提供可预测的架构,实现更安全、更可靠的 AI 编辑。我们还将介绍 Lightbuild 适合智能体使用的 CLI 工具,这些工具可用于在本地机器、团队和 CI/CD 流水线中创建可重现的 build
resolved.deps 文件中,从而绕过脚本评估。这些文件还可以在 CI/CD 构建期间用作锁定文件。query 和 resolve 等命令,可为 Agentic Workflows 输出 JSON。Lightbuild 目前在 Android Studio Canary 中提供,可供实验性使用。转写文稿
大家好
我叫 Juan Sebastian Oviedo。
我是 Android 开发者体验团队的产品经理。
今天,我和 Scott 很高兴能与大家分享 Lightbuild 的早期预览版,这是一种全新的实验性 Android 应用构建体验。
我将介绍该项目的背景、背后的理念,以及为什么我们认为现在是重新思考 Android 项目配置方式的最佳时机。
我还会介绍这种新方法如何为 Android 应用开发的未来奠定基础。
我们先来了解一下目前的工作方式。
借助 Gradle 和命令式构建脚本,开发者能够以编程方式定义几乎所有与构建相关的内容。
例如,您可以编写一个根据 Git 历史记录动态计算版本代码的逻辑,也可以定义一个转换资源的自定义任务。
// build.gradle.kts
android {
compileSdk = 37
buildTypes {
debug {
// Custom logic in build script
val commit = providers.exec {
commandLine("git", "rev-parse",
"--short", "HEAD")
}.standardOutput.asText.get()
.trim()
versionNameSuffix = "-$commit"
}
}
}除了 Gradle 之外,还有 AGP(即 Android Gradle 插件),它采用 Gradle 的通用引擎,并使其适应 Android 工具链。
Gradle 和 AGP 共同构成了一个动态系统,为当今数百万个正式版应用的 build 配置体验提供支持。
由于这种灵活性,我们的开发者工具在特定的架构生命周期内运行。
当您打开项目、切换分支或修改 build 文件时,IDE 需要有一张清晰的地图,清楚地了解您的项目配置,以便支持代码补全和代码导航等功能。
由于 build 文件是用命令式、图灵完备的语言编写的,因此 IDE 了解配置的唯一通用方法是运行 build 脚本。
在 IDE 同步期间,构建系统会经历一个配置阶段。
它会评估您的 build 脚本、执行插件逻辑并构建项目模型。
这会使 Android Studio 的项目视图与您的实际项目配置保持一致。
作为开发者,您最关心的是打造一款出色的 Android 应用并将其提供给用户。
不过,管理 build 脚本、使 AGP 保持最新状态以及使项目与 IDE 保持同步都需要花费时间和精力。
Google 和更广泛的生态系统正在开展多项举措,致力于改善这方面的开发者体验。
Declarative Gradle 和 Kotlin 工具链等计划旨在用声明式 build 配置取代可执行 build 脚本。
在 Android Studio 中,我们提供了 AGP 升级助理,可引导您完成从一个 AGP 版本升级到另一个版本所需的更改。
我们还提供了一项升级技能,智能体可以安装并遵循该技能来升级 AGP。
此外,我们还在分阶段同步和弹性同步等领域进行投入,以便开发者在同步和导入项目时获得更好的体验。
这些工作中的每一项都针对一个环节,以便开发者可以花更多时间创建应用,而减少构建项目、管理 build 文件和同步项目的时间。
在开展这些工作的同时,我们还思考了一个更广泛的问题。
如果我们从头开始重新构想项目的配置方式,会怎么样?
纵观当今现代 Android 应用的架构方式,我们发现其正朝着高度模块化的方向发生重大转变。
大型代码库通常包含数十个甚至数百个模块:界面模块、功能模块、网域模块和数据模块。
虽然根应用偶尔可能需要自定义打包,但绝大多数库模块都遵循标准化的可重复模式。
实际上,他们不需要在 build 文件中包含任意可执行代码。
他们只需要一种清晰的方式来声明其结构和依赖项。
基于这一观察,我们开发了 Lightbuild。
Lightbuild 依托于两大支柱。
首先,它使用声明式语法进行配置,从而明确区分 build 配置和逻辑。
其次,它通过提前解析依赖项,提供快速的 IDE 同步体验。
我将详细介绍每个支柱,以便您了解它们对您日常工作流程的影响。
我们先从声明式 build 配置这一支柱开始。
请查看屏幕左侧的文件。
这表示当前模块的配置。
// build.gradle.kts
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.compose)
}
val gitCommit = providers.exec {
commandLine("git", "rev-parse", "--short", "HEAD")
}.standardOutput.asText.get().trim()
android {
namespace = "com.example.app"
compileSdk = 37
defaultConfig {
minSdk = 24
buildConfigField("String", "GIT_COMMIT",
"\"$gitCommit\"")
}
buildFeatures {
compose = true
buildConfig = true
}
}
dependencies {
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.graphics)
...由于此文件是可执行代码,因此它可以包含局部变量定义、对自定义辅助函数或惯例插件的调用、条件语句和内嵌任务定义。
为了让 IDE 知道此配置解析为什么,它实际上需要运行脚本。
现在,请查看屏幕右侧的文件。
这是类似模块的 Lightbuild 配置。
# lightbuild.yaml
group: "com.example"
version: "1.0.0"
platform: android
type: library
android:
namespace: "com.example.app"
compileSdk:
version:
release:
apiLevel: 37
minSdk:
version:
release:
apiLevel: 24
dependencies:
maven:
- androidx.core:core-ktx:1.10.1
- androidx.lifecycle:lifecycle-runtime-ktx:2.6.1
- androidx.activity:activity-compose:1.8.0此文件纯粹是声明式数据。
没有局部变量定义,没有对自定义辅助函数的调用,也没有条件语句或内嵌任务定义。
它明确说明了模块的类型、其 SDK 目标及其依赖项。
如果新工程师加入您的团队并打开此文件,他们无需解读任何自定义构建惯用语,因为每个 Lightbuild 项目中的每个 Lightbuild 模块都使用相同的、一目了然的语言。
配置与逻辑的分离不仅极大提升了人类的可读性,更对 Agent Assist 工作流带来了颠覆性的变革。
随着开发者将智能体纳入其工作流,这些智能体需要能够读取和修改 build 配置。
不过,除了实际构建项目之外,他们无法预测配置更改的副作用。
向配置文件添加一行代码可能会与项目中其他位置动态计算的变量或惯例插件发生冲突。
在 Lightbuild 中,项目的配置是一个确定性的静态数据图。
这意味着智能体可以检查该图,了解模块拓扑,并使用已知架构对其进行修改。
智能体只需读取和写入结构化数据,即可查看模块列表、依赖项及其版本列表,更新依赖项,甚至添加全新的功能模块。
它使项目的配置成为智能体辅助工作流的一等公民。
此时我想强调的另一个细节是,Lightbuild 不是构建系统的插件,而是构建引擎的接口。
我们今天发布的 Lightbuild 预览版在底层使用 Gradle 作为执行引擎。
不过,我们在 Lightbuild 中采用的架构方法使得 Android Studio(即 IDE)能够将项目视为 Lightbuild 项目。
现在,我们来介绍第二个支柱,它完全关乎开发者工作效率和流程:快速 IDE 同步。
即使开发者将智能体纳入其工作流,Android Studio 仍然是执行交互式任务的重要工具,例如,查看 Compose 界面预览、在调试程序中单步调试代码以调查问题,或使用性能分析器分析性能。
由于项目的配置是确定性的静态数据图,因此 IDE 无需经历脚本评估阶段或执行插件代码即可了解您的项目。
无论您是打开项目、修改 build 文件还是切换分支,IDE 都能快速处理这些信息,让您直接使用工具。
Lightbuild 之所以能实现如此高的速度和可靠性,原因之一在于其处理依赖项的方式。
通常,当您声明依赖项时,构建系统需要遍历传递依赖项树,以收集版本信息并解决冲突。
在 Lightbuild 中,每当依赖项发生变化时,该工具都会自动计算传递依赖项信息,并将其写入每个模块目录中的 resolved.deps 文件。
这样一来,当项目日后再次打开或重新构建时,IDE 和构建工具都不需要重新计算该传递依赖项信息。
我们建议您将这些文件签入源代码控制系统,这样当您将更改发送给团队成员进行审核时,他们就可以查看传递依赖项信息是否有任何更改,而且他们的机器无需重新计算该传递依赖项信息。
此外,通过将这些信息签入源代码控制系统,您、您的团队成员和 CI/CD 流水线每次都可以使用相同的依赖项版本进行构建。
我们很高兴地宣布,Lightbuild 现已在 Android Studio Canary 中作为早期预览版提供,并且还可通过独立的 CLI 工具使用。
请注意,此版本的 Lightbuild 尚处于实验阶段,因此不具备构建和维护生产应用所需的所有功能。
请继续使用 AGP 和 Gradle 构建正式版应用,同时在演示和实验性项目中试用 Lightbuild。
您可以立即访问 goo.gle/lightbuild 开始使用,我们期待收到您的反馈,以便您帮助我们不断改进产品。
现在,我将把演讲时间交给 Scott,他将为大家演示 Lightbuild 的实际应用。
谢谢,Juan。
我叫 Scott Myers。
我是 Android 开发者关系工程师。我们一直在开发 Lightbuild,今天很高兴能向大家展示如何实际体验和使用它。
因此,您首先需要访问 Juan 提到的链接 goo.gle/lightbuild,或者直接搜索 Lightbuild。
然后,您应该会进入此文档页面,其中包含您开始使用所需的所有步骤,包括安装 Canary 版 Android Studio,因为请记住,此功能仍处于预览阶段,尚未在稳定版 Android Studio 中提供。
此外,您还可以安装 Lightbuild CLI,以便充分利用 Lightbuild 的智能体体验。
因此,在您完成设置后,就像我在我的机器上所做的那样,我运行了最新的 Android Studio Canary。
现在,我将逐步演示如何实际创建一个 Lightbuild 演示项目,并向您展示如何立即动手实践。
首先,我将前往“New Project”向导。
我将选择“Empty Activity”,然后点击“Next”。
现在,您会看到这是正常的项目详情屏幕,但在底部有一个新选项。
在 build 配置语言下,您可以选择 Lightbuild。
在您点击“完成”之前,系统会提醒您,这是一项实验性功能。
您会看到,我们的项目会立即与 IDE 同步并启动运行,随时可供我们使用。
现在,我们来看看典型 Lightbuild 项目的项目结构。
这是创建新应用时获得的默认 Android 模板。
它只显示“Hello, world!”
因此,这是一种简单的入门方式。
您会看到,在项目的根级别,有一个名为 project.lightbuild.yaml 的文件。
这样,我们就可以为整个项目进行全局设置。
因此,您可以在这里为项目命名,列出项目中的所有模块,指定要从哪些外部代码库拉取依赖项,以及要全局应用于项目不同部分的不同版本。
可以看到,这里有两个模块:app 和 app-core。
这是因为所有 Lightbuild 项目都至少有两个模块。
这是因为必须至少有一个应用模块和至少一个库模块。
我稍后会解释原因。
首先,我们来看看这个应用模块。
如果我在这里展开它,您会看到在模块的根级别有一个 lightbuild.yaml 文件,每个模块都会有这个文件。
这里有一些基本信息。
我们正在开发 Android 平台,
此模块的类型为 application。
您可以列出 SDK 目标版本、应用 ID 等,然后列出此应用模块所依赖的所有模块。
现在,这个应用模块与标准 Android 模板略有不同,有趣的是,其源代码文件夹中没有任何内容。
因此,在 Lightbuild 中,应用模块不包含任何源代码。
这是因为此应用模块不会经常更改,但需要一些时间来构建。
因此,如果您将所有应用代码都保留在库模块中,那么每次构建都会更快,因为它不必重新计算和重新打包应用模块中的所有内容。
接下来,我们来看看 app-core 模块,它是此应用模块所依赖的库模块。
您可以看到,此模块中确实有源代码。
这里有我们的主要逻辑。
它只显示“Hello, world!”
如果我查看此模块中的 lightbuild.yaml 文件,您可以看到它的类型是库。
这里还设置了一些 SDK 版本号,并列出了此模块中的所有外部依赖项。
它还声明了单元测试和插桩测试。
接下来,我们来看看您可能已经注意到的另一个文件,Juan 之前也提到过,每个模块中都有一个 resolved.deps 文件。
此文件由 Lightbuild 自动生成。
这意味着您不应手动修改该文件,但它会包含此模块中所有已解析依赖项的列表。
因此,您的所有依赖项和传递依赖项都已在此处。
这意味着,当您在 IDE 中打开应用或要进行构建时,您无需在此时弄清楚这一点,因为它已经提前弄清楚并签入到您的项目中。
接下来,我们通过一个示例来看看 Lightbuild 如何通过项目同步功能提升开发者体验。
现在,我回到了根级 project.lightbuild.yaml,这里的全局设置列出了我应用中的所有模块。
通常,在修改配置或 build 配置文件时,您会注意到系统会显示一个同步按钮,该按钮需要一些时间来处理所有这些内容,因为正如 Juan 所说,它必须通过执行所有 build 脚本来执行部分 build,然后将该模型与 IDE 同步。
因此,我们来对这个小项目进行一次相当重大的更改。
我们只需删除其中一个模块。
您可以看到,在我点击保存按钮之前,此模块已被 IDE 识别,因为您可以看到它在这里显示为粗体。
它识别出了源目录和测试目录。
完成更改并点击保存后,您可以看到该模块立即从项目中消失了。
如果我再次撤消该更改并点击“保存”,它会同样快速地恢复。
这就是 Lightbuild 为项目带来的快速 IDE 同步示例。
此外,您还可以执行您在 Android Studio 中习惯使用的所有常规命令。
您可以点击“运行”按钮。
它会编译您的应用并在此处运行。
这个特定的演示应用并不大,但我会放大它,以显示它在说“Hello Android”。
您可以像往常一样运行测试,只需进入测试文件,然后像这样从边线运行即可。
实际上,它在后台调用 Lightbuild 来运行这些测试。
因此,所有这些都按预期运行。
接下来,我想展示一下,如果您不使用 Android Studio,而是使用其他智能体流程,该如何使用 Lightbuild。
为此,我们可以使用命令行。
因此,在 Lightbuild 的 CLI 中,我只需运行基本帮助命令。
是的,您今天在这个项目中看到的所有拼写错误都是我造成的。
Lightbuild 将内置多个不同的命令,例如…哦,抱歉,我是在项目之外运行此命令。
它一直在告诉我这一点。
是的,现场演示。
首先,在运行任何 Lightbuild 命令之前,我需要创建一个 Lightbuild 项目。
为此,我将使用 android create,这是我们用于创建新项目模板的标准命令行工具。
我将使用 empty-activity-lightbuild 模板,将应用命名为 Lightbuild Demo,并将其放入演示输出目录中。
现在,如果我进入该目录并输入 lightbuild -h,就会看到 Lightbuild 提供的所有不同命令。
因此,我们提供了许多您期望的功能:build、clean、test。
但也有一些新功能。
有一个 resolve 命令,它实际上会遍历项目的依赖项,并生成我之前展示的那些 resolved.deps 文件。
还有一个查询命令,可让您和您的代理更好地了解项目结构。
首先,我来展示一下新的查询命令。
因此,如果我使用命令行查看此项目的结构,会发现它与我在 Android Studio 演示中使用的项目结构相同。
但这些信息并不能让我了解项目的布局。
因此,如果我执行 Lightbuild 查询,并查看这个应用模块,我将使用这个特殊语法来表示“显示此应用模块下的所有内容”。
它会显示一个表格,其中列出了我可以在应用模块中构建的不同目标。
因此,它包含调试版和发布版二进制文件的 APK 和 AAB。
如果我只运行应用目标,它会构建所有内容。
此外,还可以将输出指定为 JSON。
这样一来,哦,抱歉。
同样,我今天的所有拼写错误都是真实的。
在这里,我可以使用交互式 Lightbuild 帮助,确保我的操作正确无误。
呃…
是的,应该可以。
好了
如果有人能告诉我我哪里做错了,请在这次演讲后告诉我。
不过,除了此表格之外,您还可以将 Lightbuild 命令输出为 JSON,这样对智能体来说更简单。
--format json…
哦,格式。
哦,抱歉。
谢谢。
这是开发者的实时反馈。
谢谢。
好的,是的,还有一个输出命令,我也可以演示一下,可能需要您的帮助。
因此,如果我输出文件而不是目标,则会显示 build 中所有不同二进制文件的位置。
因此,这只是您和您的智能体能够使用 Lightbuild 从命令行探索和更好地了解项目的一种方式。
然后,显然还有我之前提到的 lightbuild resolve 命令,它会生成所有 resolved.deps 文件。
该版本还包含一些额外功能。
例如,有一个 locked dependencies 参数,使用该参数时,它不会直接生成这些文件,而是会将文件内容与您在 build 配置中声明的依赖项进行比较。
如果存在任何差异,它会通知您,并导致构建失败。
因此,这些已解析的依赖项文件可以充当锁定文件。例如,如果您在 CI/CD 系统中构建应用以进行生产,您会希望知道在构建过程中是否有任何依赖项发生变化。
此外,Lightbuild 还内置了测试功能,您也可以直接从命令行运行此库模块下的所有测试。
还有一件事我想展示一下。
只是它还提供了帮助,其中包含您可以使用的所有参数和子命令的完整列表。
这是智能体可以使用的工具,因此这是智能体的输出。
好了
现在,我们已经了解了一些使用 Lightbuild 的简单项目,接下来我想展示一个更具挑战性的项目。
Android 团队提供了一个名为“Now in Android”的示例应用,其中包含一些最新的 Android 功能,并展示了如何使用这些功能以及一些最佳实践。
这是一款高度模块化的应用。
它被拆分为大约 32 个不同的模块。
这就是它在 GitHub 中的样子。
不过,我将切换回 Android Studio,因为我之前已经使用过我的代理。
我当时正在使用 Gemini Argon 将 Now in Android 项目转换为 Lightbuild。
所以,这不是我们发布的内容,只是我自己做的一个实验。
您可以看到,尽管我的 project.lightbuild.yaml 中的这个项目包含所有这些不同的模块,但它仍然以极快的速度打开,并以非常快的速度与此 IDE 同步。
关于这个特定项目,您还需要注意的一点是,它使用了一些 build 变种或特殊 build 配置,以便同时拥有演示 build 和正式 build,每个 build 呈现应用的方式略有不同。
演示版本会使用内置数据源,而生产版本实际上会连接到网络并拉取数据。
为了通过 Lightbuild 实现这一点,它处理不同 build 配置或产品变种的方式是:您进入应用模块,并指定该应用模块(例如,演示应用)。
因此,它将依赖于所有核心组件,但也会拉取仅适用于应用演示版本的特殊设置。
这样,您就可以拥有多个应用模块。
每个变种都可以代表一个应用版本,例如应用的付费版或免费版。
而且,您仍然可以享受应用模块作为空壳的好处,它本身不包含任何代码,构建速度更快,并且每当库发生更改时,只需重新编译这些库即可再次构建应用。
我们来看看这个 Now in Android 应用是否已成功使用 Lightbuild 构建,以及其 32 个不同的模块是否都已成功构建。
是的,就是这个。
我想指出的是,通过此类实验,我们还了解到 Lightbuild 并不总是支持您可能需要的每项构建功能。
例如,Now in Android 应用依赖于 Hilt 插件才能使用 Hilt,而 Lightbuild 尚不支持此类插件。
因此,我的智能体能够找到一种解决方法,在不大幅更改项目本身代码的情况下,仍然在项目中添加了 Hilt 支持。
例如,所有这些模块中的所有代码都与原始代码库相同。
但有一点一定要记住,当您自己实验和演示 Lightbuild 时,当前项目中的某些 build 配置可能不受支持。
因此,Lightbuild 目前仍处于预览阶段,仅在 Android Studio Canary 中提供。
// build.gradle.kts
android {
compileSdk = 37
buildTypes {
debug {
// Custom logic in build script
val commit = providers.exec {
commandLine("git", "rev-parse",
"--short", "HEAD")
}.standardOutput.asText.get()
.trim()
versionNameSuffix = "-$commit"
}
}
}// build.gradle.kts
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.compose)
}
val gitCommit = providers.exec {
commandLine("git", "rev-parse", "--short", "HEAD")
}.standardOutput.asText.get().trim()
android {
namespace = "com.example.app"
compileSdk = 37
defaultConfig {
minSdk = 24
buildConfigField("String", "GIT_COMMIT",
"\"$gitCommit\"")
}
buildFeatures {
compose = true
buildConfig = true
}
}
dependencies {
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.graphics)
...# lightbuild.yaml
group: "com.example"
version: "1.0.0"
platform: android
type: library
android:
namespace: "com.example.app"
compileSdk:
version:
release:
apiLevel: 37
minSdk:
version:
release:
apiLevel: 24
dependencies:
maven:
- androidx.core:core-ktx:1.10.1
- androidx.lifecycle:lifecycle-runtime-ktx:2.6.1
- androidx.activity:activity-compose:1.8.0