Bazel 教程:构建 Android 应用

报告问题 查看源代码

注意:使用 Bazel 构建 Android 应用存在一些已知限制。访问 GitHub 团队 Android 常用表,查看已知问题列表。尽管 Bazel 团队和开源软件 (OSS) 贡献者一直在积极解决已知问题,但用户应该知道,Android Studio 并未正式支持 Bazel 项目。

本教程介绍了如何使用 Bazel 构建简单的 Android 应用。

Bazel 支持使用 Android 规则构建 Android 应用。

本教程适用于 Windows、macOS 和 Linux 用户,无需具备 Bazel 或 Android 应用开发经验。您无需在本教程中编写任何 Android 代码。

学习内容

在本教程中,您将学习如何:

  • 安装 Bazel 和 Android Studio 并下载示例项目,以设置环境。
  • 设置一个 Bazel 工作区,其中包含应用的源代码,以及一个标识工作区目录顶层的 WORKSPACE 文件。
  • 更新 WORKSPACE 文件,以包含对所需外部依赖项(如 Android SDK)的引用。
  • 创建 BUILD 文件。
  • 使用 Bazel 构建应用。
  • 在 Android 模拟器或实体设备上部署并运行应用。

准备工作

安装 Bazel

在开始学习本教程之前,请安装以下软件:

  • Bazel.。如需安装,请按照安装说明操作。
  • Android Studio -如需安装,请按照相应步骤下载 Android Studio。 执行设置向导以下载 SDK 并配置您的环境。
  • (可选)Git。使用 git 下载 Android 应用项目。

获取示例项目

对于示例项目,请使用 Bazel 示例代码库中的基本 Android 应用项目。

此应用有一个按钮,点击后会输出一条问候语:

按钮问候语

图 1. Android 应用按钮问候语。

使用 git 克隆代码库(或直接下载 ZIP 文件):

git clone https://github.com/bazelbuild/examples

本教程的示例项目位于 examples/android/tutorial 中。在本教程的其余部分,您将在此目录中执行命令。

查看源文件

查看应用的源文件。

.
├── README.md
└── src
    └── main
        ├── AndroidManifest.xml
        └── java
            └── com
                └── example
                    └── bazel
                        ├── AndroidManifest.xml
                        ├── Greeter.java
                        ├── MainActivity.java
                        └── res
                            ├── layout
                            │   └── activity_main.xml
                            └── values
                                ├── colors.xml
                                └── strings.xml

关键文件和目录如下:

名称 位置
Android 清单文件 src/main/AndroidManifest.xmlsrc/main/java/com/example/bazel/AndroidManifest.xml
Android 源文件 src/main/java/com/example/bazel/MainActivity.javaGreeter.java
资源文件目录 src/main/java/com/example/bazel/res/

使用 Bazel 构建

设置工作区

工作区是包含一个或多个软件项目的源文件的目录,其根目录下有一个 WORKSPACE 文件。

WORKSPACE 文件可能为空,也可能包含对构建项目所需的外部依赖项的引用。

首先,运行以下命令以创建空的 WORKSPACE 文件:

操作系统 命令
Linux、macOS touch WORKSPACE
Windows(命令提示符) type nul > WORKSPACE
Windows (PowerShell) New-Item WORKSPACE -ItemType file

运行 Bazel

您现在可以使用以下命令检查 Bazel 是否正常运行:

bazel info workspace

如果 Bazel 输出了当前目录的路径,那就大功告成了!如果 WORKSPACE 文件不存在,您可能会看到如下错误消息:

ERROR: The 'info' command is only supported from within a workspace.

与 Android SDK 集成

Bazel 需要运行 Android SDK 构建工具来构建应用。这意味着,您需要向 WORKSPACE 文件添加一些信息,以便 Bazel 知道在哪里可以找到这些信息。

将以下行添加到 WORKSPACE 文件中:

android_sdk_repository(name = "androidsdk")

这会在 ANDROID_HOME 环境变量引用的路径中使用 Android SDK,并自动检测该位置中安装的最高 API 级别和最新版本的构建工具。

您可以将 ANDROID_HOME 变量设置为 Android SDK 的位置。使用 Android Studio 的 SDK 管理器查找已安装 SDK 的路径。 假设 SDK 安装到默认位置,您可以使用以下命令设置 ANDROID_HOME 变量:

操作系统 命令
Linux export ANDROID_HOME=$HOME/Android/Sdk/
macOS export ANDROID_HOME=$HOME/Library/Android/sdk
Windows(命令提示符) set ANDROID_HOME=%LOCALAPPDATA%\Android\Sdk
Windows (PowerShell) $env:ANDROID_HOME="$env:LOCALAPPDATA\Android\Sdk"

上述命令仅为当前的 shell 会话设置变量。如需永久保留这些软件包,请运行以下命令:

操作系统 命令
Linux echo "export ANDROID_HOME=$HOME/Android/Sdk/" >> ~/.bashrc
macOS echo "export ANDROID_HOME=$HOME/Library/Android/Sdk/" >> ~/.bashrc
Windows(命令提示符) setx ANDROID_HOME "%LOCALAPPDATA%\Android\Sdk"
Windows (PowerShell) [System.Environment]::SetEnvironmentVariable('ANDROID_HOME', "$env:LOCALAPPDATA\Android\Sdk", [System.EnvironmentVariableTarget]::User)

您还可以通过添加 pathapi_levelbuild_tools_version 属性来明确指定 Android SDK 的绝对路径、API 级别和要使用的构建工具版本。如果未指定 api_levelbuild_tools_versionandroid_sdk_repository 规则将使用 SDK 中提供的相应最新版本。您可以指定这些属性的任意组合,只要它们出现在 SDK 中即可,例如:

android_sdk_repository(
    name = "androidsdk",
    path = "/path/to/Android/sdk",
    api_level = 25,
    build_tools_version = "30.0.3"
)

请注意,在 Windows 上,path 属性必须使用混合样式路径,即带有正斜杠的 Windows 路径:

android_sdk_repository(
    name = "androidsdk",
    path = "c:/path/to/Android/sdk",
)

可选:如果您要将原生代码编译到 Android 应用中,还需要下载 Android NDK,并向 WORKSPACE 文件添加下面这行代码,以告知 Bazel 的位置:

android_ndk_repository(name = "androidndk")

android_sdk_repository 类似,Android NDK 的路径默认通过 ANDROID_NDK_HOME 环境变量推断出来。您还可以使用 android_ndk_repository 中的 path 属性明确指定路径。

如需了解详情,请参阅将 Android 原生开发套件与 Bazel 搭配使用

api_level 是 SDK 和 NDK 的目标 Android API 版本,例如,23 表示 Android 6.0,25 表示 Android 7.1。如果未明确设置,api_level 会默认为 android_sdk_repositoryandroid_ndk_repository 的可用最高 API 级别。

无需将 SDK 和 NDK 的 API 级别设置为相同的值。此页面包含从 Android 版本到支持 NDK 的 API 级别的映射。

创建 BUILD 文件

BUILD 文件描述了一组 build 输出(例如来自 aapt 的已编译 Android 资源或来自 javac 的类文件)与其依赖项之间的关系。这些依赖项可能是工作区中的源文件(Java、C++)或其他 build 输出。BUILD 文件采用一种名为 Starlark 的语言编写。

BUILD 文件是 Bazel 中称为“软件包层次结构”的概念的一部分。软件包层次结构是一种逻辑结构,叠加在工作区中的目录结构之上。每个软件包都是一个目录(及其子目录),其中包含一组相关的源文件和一个 BUILD 文件。此外,该软件包还包含所有子目录,但不包括包含自己的 BUILD 文件的子目录。软件包名称BUILD 文件的路径(相对于 WORKSPACE)。

请注意,Bazel 的软件包层次结构在概念上与 BUILD 文件所在 Android 应用目录的 Java 软件包层次结构不同,但目录的组织方式可能完全相同。

对于本教程中的简单 Android 应用,src/main/ 中的源文件包含一个 Bazel 软件包。更复杂的项目可能会有多个嵌套软件包。

添加 android_library 规则

一个 BUILD 文件包含多种不同类型的 Bazel 声明。最重要的类型是构建规则,它告诉 Bazel 如何根据一组源文件或其他依赖项构建中间或最终软件输出。Bazel 提供了两种构建规则:android_libraryandroid_binary,可用于构建 Android 应用。

在本教程中,您将首先使用 android_library 规则指示 Bazel 根据应用源代码和资源文件构建 Android 库模块。然后,您将使用 android_binary 规则告知 Bazel 如何构建 Android 应用软件包。

src/main/java/com/example/bazel 目录中创建一个新的 BUILD 文件,并声明一个新的 android_library 目标:

src/main/java/com/example/bazel/BUILD:

package(
    default_visibility = ["//src:__subpackages__"],
)

android_library(
    name = "greeter_activity",
    srcs = [
        "Greeter.java",
        "MainActivity.java",
    ],
    manifest = "AndroidManifest.xml",
    resource_files = glob(["res/**"]),
)

android_library 构建规则包含一组属性,用于指定 Bazel 从源文件构建库模块所需的信息。另请注意,规则的名称为 greeter_activity。您将在 android_binary 规则中引用使用此名称作为依赖项的规则。

添加 android_binary 规则

android_binary 规则为您的应用构建 Android 应用软件包(.apk 文件)。

src/main/ 目录中创建一个新的 BUILD 文件,并声明一个新的 android_binary 目标:

src/main/BUILD:

android_binary(
    name = "app",
    manifest = "AndroidManifest.xml",
    deps = ["//src/main/java/com/example/bazel:greeter_activity"],
)

此处,deps 属性引用了您添加到上述 BUILD 文件的 greeter_activity 规则的输出。这意味着,Bazel 在构建此规则的输出时会先检查 greeter_activity 库规则的输出是否已构建且是否处于最新状态。否则,Bazel 会构建该文件,然后使用该输出来构建应用软件包文件。

现在,保存并关闭该文件。

构建应用

尝试构建应用!运行以下命令以构建 android_binary 目标:

bazel build //src/main:app

build 子命令指示 Bazel 构建后面的目标。系统会将目标指定为 BUILD 文件中的构建规则的名称,以及相对于工作区目录的软件包路径。在此示例中,目标是 app,软件包路径为 //src/main/

请注意,有时您可以省略软件包路径或目标名称,具体取决于命令行中的当前工作目录和目标的名称。如需详细了解目标标签和路径,请参阅标签

Bazel 将开始构建示例应用。在构建流程中,其输出将如下所示:

INFO: Analysed target //src/main:app (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //src/main:app up-to-date:
  bazel-bin/src/main/app_deploy.jar
  bazel-bin/src/main/app_unsigned.apk
  bazel-bin/src/main/app.apk

找到构建输出

Bazel 会将中间构建操作和最终构建操作的输出放在一组针对每个用户、每个工作区的输出目录中。这些目录从项目目录顶层的以下位置进行符号链接,其中 WORKSPACE 为:

  • bazel-bin 会存储二进制可执行文件和其他可运行 build 输出
  • bazel-genfiles 存储由 Bazel 规则生成的中间源文件
  • bazel-out 会存储其他类型的构建输出

Bazel 会将使用 android_binary 规则生成的 Android .apk 文件存储在 bazel-bin/src/main 目录中,其中子目录名称 src/main 派生自 Bazel 软件包的名称。

在命令提示符处,列出此目录的内容并找到 app.apk 文件:

操作系统 命令
Linux、macOS ls bazel-bin/src/main
Windows(命令提示符) dir bazel-bin\src\main
Windows (PowerShell) ls bazel-bin\src\main

运行应用

现在,您可以从命令行使用 bazel mobile-install 命令将应用部署到已连接的 Android 设备或模拟器。此命令使用 Android 调试桥 (adb) 与设备进行通信。在部署之前,您必须按照 Android 调试桥中的说明将设备设置为使用 adb。您还可以选择在 Android Studio 中包含的 Android 模拟器上安装应用。在执行下面的命令之前,请确保模拟器正在运行。

输入以下内容:

bazel mobile-install //src/main:app

接下来,找到并启动“Bazel 教程应用”:

Bazel 教程应用

图 2. Bazel 教程应用。

恭喜!您刚刚安装了您的第一个由 Bazel 构建的 Android 应用。

请注意,mobile-install 子命令还支持 --incremental 标志,该标志可用于仅部署应用中自上次部署以来发生更改的部分。

此外,它还支持 --start_app 标志,以便在安装应用后立即启动应用。

深入阅读

如需了解详情,请参阅以下页面:

祝您使用愉快!