Android 构建系统会编译应用资源和源代码,然后将它们打包到 APK 或 Android App Bundle 中,供您测试、部署、签名和分发。Android Studio 会使用高级构建工具包 Gradle 自动执行和管理构建流程,同时也允许您定义灵活的自定义 build 配置。每个 build 配置均会定义自己的一组代码和资源,并重复利用所有应用版本共用的部分。Android Plugin for Gradle 与该构建工具包搭配使用,提供专用于构建和测试 Android 应用的流程和可配置设置。
Gradle 和 Android 插件独立于 Android Studio 运行。这意味着,您可以在 Android Studio 内、计算机上的命令行或未安装 Android Studio 的计算机(如持续集成服务器)上构建 Android 应用。如果您不使用 Android Studio,可以学习如何从命令行构建和运行应用。无论您是从命令行、在远程计算机上还是使用 Android Studio 构建项目,构建的输出都相同。
注意:由于 Gradle 和 Android 插件独立于 Android Studio 运行,因此您需要单独更新构建工具。请阅读版本说明,了解如何更新 Gradle 和 Android 插件。
Android 构建系统非常灵活,可让您在不修改应用核心源代码文件的情况下执行自定义构建配置。本部分将介绍 Android 构建系统的工作原理,以及它如何帮助您对多个构建配置进行自定义和自动化处理。如果您只想详细了解如何部署应用,请参阅在 Android Studio 中构建和运行应用。如需立即开始使用 Android Studio 创建自定义构建配置,请参阅配置构建变体。
构建流程
构建流程涉及许多将项目转换成 Android 应用软件包 (APK) 或 Android App Bundle (AAB) 的工具和流程。构建流程非常灵活,因此了解它的一些底层工作原理会很有帮助。
图 1. 典型 Android 应用模块的构建流程。
典型 Android 应用模块的构建流程(如图 1 所示)按照以下常规步骤执行:
- 编译器将您的源代码转换成 DEX 文件(Dalvik 可执行文件,其中包括在 Android 设备上运行的字节码),并将其他所有内容转换成编译后的资源。
- 打包器将 DEX 文件和编译后的资源组合成 APK 或 AAB(具体取决于所选的 build 目标)。 必须先为 APK 或 AAB 签名,然后才能将应用安装到 Android 设备或分发到 Google Play 等商店。
- 打包器使用调试或发布密钥库为 APK 或 AAB 签名:
- 如果您构建的是调试版应用(即专门用来测试和分析的应用),则打包器会使用调试密钥库为应用签名。Android Studio 会自动使用调试密钥库配置新项目。
- 如果您构建的是打算对外发布的发布版应用,则打包器会使用发布密钥库(您需要进行配置)为应用签名。如需创建发布密钥库,请参阅在 Android Studio 中为应用签名。
- 在生成最终 APK 之前,打包器会使用 zipalign 工具对应用进行优化,以减少其在设备上运行时所占用的内存。
构建流程结束时,您将获得应用的调试版或发布版 APK/AAB,以用于部署、测试或向外部用户发布。
Android build 术语表
Gradle 和 Android 插件可帮助您完成以下方面的构建配置:
- 构建类型
- build 类型定义 Gradle 在构建和打包应用时使用的某些属性,通常针对开发生命周期的不同阶段进行配置。例如,调试 build 类型支持调试选项,并会使用调试密钥为应用签名;而发布 build 类型则会缩减应用、对应用进行混淆处理,并使用发布密钥为应用签名以进行分发。如需构建应用,您必须至少定义一个 build 类型。Android Studio 默认会创建调试 build 类型和发布 build 类型。如需开始为应用自定义打包设置,请了解如何配置构建类型。
- 产品变种
- 产品变种代表您可以向用户发布的不同应用版本,如免费版应用和付费版应用。您可以自定义产品变种以使用不同的代码和资源,同时共享并重用所有应用版本共用的部分。产品变种是可选的,您必须手动创建。如需开始创建应用的不同版本,请了解如何配置产品变种。
- 构建变体
- 构建变体是构建类型与产品变种的交叉产物,也是 Gradle 用来构建应用的配置。利用构建变体,您可以在开发期间构建产品变种的调试版本,或者构建产品变种的已签名发布版本以供分发。虽然您无法直接配置 build 变体,但可以配置组成它们的 build 类型和产品变种。创建额外的构建类型或产品变种也会创建额外的构建变体。如需了解如何创建和管理构建变体,请参阅配置构建变体概览。
- 清单条目
- 您可以在构建变体配置中为清单文件中的某些属性指定值。这些 build 值会替换清单文件中的现有值。如果您要为应用生成多个变体,让每一个变体都具有不同的应用名称、最低 SDK 版本或目标 SDK 版本,便可运用这一技巧。当存在多个清单时,Gradle 会合并清单设置。
- 依赖项
- 构建系统会管理来自本地文件系统以及来自远程代码库的项目依赖项。这样一来,您就不必手动搜索、下载依赖项的二进制文件包以及将它们复制到项目目录中。如需了解详情,请参阅添加 build 依赖项。
- 签名
- 构建系统既允许您在 build 配置中指定签名设置,也可以在构建流程中自动为应用签名。构建系统通过已知凭据使用默认密钥和证书为调试版本签名,以避免在构建时提示输入密码。除非您为此构建明确定义签名配置,否则,构建系统不会为发布版本签名。如果您没有发布密钥,可以按为应用签名中所述生成一个。
- 代码和资源缩减
- 构建系统允许您为每个构建变体指定不同的 ProGuard 规则文件。在构建应用时,构建系统会应用一组适当的规则以使用其内置的缩减工具(如 R8)缩减您的代码和资源。
- 多 APK 支持
- 通过构建系统可以自动构建不同的 APK,并让每个 APK 只包含特定屏幕密度或应用二进制接口 (ABI) 所需的代码和资源。如需了解详情,请参阅构建多个 APK。请注意,我们建议的方法是发布单个 AAB,因为它除了让您可以按屏幕密度和 ABI 进行拆分以外,还可以让您按语言进行拆分,同时还可以降低因必须上传多个工件到 Google Play 所造成的复杂性。
build 配置文件
创建自定义构建配置需要您对一个或多个构建配置文件(即 build.gradle
文件)做出更改。这些纯文本文件使用领域特定语言 (DSL) 以 Groovy 描述和操纵构建逻辑,其中 Groovy 是一种适用于 Java 虚拟机 (JVM) 的动态语言。您无需了解 Groovy 便可开始配置构建,因为 Android Plugin for Gradle 引入了您需要的大多数 DSL 元素。如需详细了解 Android 插件 DSL,请参阅 DSL 参考文档。
开始新项目时,Android Studio 会自动为您创建其中的部分文件(如图 2 所示),并为其填充合理的默认值。
图 2. Android 应用模块的默认项目结构。
有一些 Gradle 构建配置文件是 Android 应用的标准项目结构的组成部分。您必须了解其中每个文件的范围和用途及其应定义的基础 DSL 元素,才能着手配置构建。
Gradle 设置文件
settings.gradle
文件位于项目的根目录下,用于定义项目级代码库设置,并告知 Gradle 在构建应用时应将哪些模块包含在内。对于大多数项目,该文件默认如下所示:
Groovy
pluginManagement { /** * The pluginManagement {repositories {...}} block configures the * repositories Gradle uses to search or download the Gradle plugins and * their transitive dependencies. Gradle pre-configures support for remote * repositories such as JCenter, Maven Central, and Ivy. You can also use * local repositories or define your own remote repositories. The code below * defines the Gradle Plugin Portal, Google's Maven repository, * and the Maven Central Repository as the repositories Gradle should use to look for its dependencies. */ repositories { gradlePluginPortal() google() mavenCentral() } } dependencyResolutionManagement { /** * The dependencyResolutionManagement { repositories {...}} * block is where you configure the repositories and dependencies used by * all modules in your project, such as libraries that you are using to * create your application. However, you should configure module-specific * dependencies in each module-level build.gradle file. For new projects, * Android Studio includes Google's Maven repository * and the Maven Central Repository by * default, but it does not configure any dependencies (unless you select a * template that requires some). */ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() } } rootProject.name = "My Application" include ‘:app’
Kotlin
pluginManagement { /** * The pluginManagement {repositories {...}} block configures the * repositories Gradle uses to search or download the Gradle plugins and * their transitive dependencies. Gradle pre-configures support for remote * repositories such as JCenter, Maven Central, and Ivy. You can also use * local repositories or define your own remote repositories. The code below * defines the Gradle Plugin Portal, Google's Maven repository, * and the Maven Central Repository as the repositories Gradle should use to look for its dependencies. */ repositories { gradlePluginPortal() google() mavenCentral() } } dependencyResolutionManagement { /** * The dependencyResolutionManagement { repositories {...}} * block is where you configure the repositories and dependencies used by * all modules in your project, such as libraries that you are using to * create your application. However, you should configure module-specific * dependencies in each module-level build.gradle file. For new projects, * Android Studio includes Google's Maven repository and the * Maven Central Repository by * default, but it does not configure any dependencies (unless you select a * template that requires some). */ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() } } rootProject.name = "My Application" include(":app")
不过,多模块项目需要指定应包含在最终 build 中的每个模块。
顶层 build 文件
顶层 build.gradle
文件位于项目的根目录下,用于定义适用于项目中所有模块的依赖项。默认情况下,顶层 build 文件使用 plugins
代码块定义项目中所有模块共用的 Gradle 依赖项。此外,顶层 build 文件还包含用于清理 build 目录的代码。以下代码示例说明了创建新项目后可在顶层 build.gradle
文件中找到的默认设置和 DSL 元素。
Groovy
plugins { /** * You should use `apply false` in the top-level build.gradle file * to add a Gradle plugin as a build dependency, but not apply it to the * current (root) project. You should not use `apply false` in sub-projects. * For more information, see * Applying external plugins with same version to subprojects. */ id 'com.android.application' version '7.2' apply false id 'com.android.library' version '7.2' apply false id 'org.jetbrains.kotlin.android' version '1.6.10' apply false } task clean(type: Delete) { delete rootProject.buildDir }
Kotlin
plugins { /** * You should use `apply false` in the top-level build.gradle file * to add a Gradle plugin as a build dependency, but not apply it to the * current (root) project. You should not use `apply false` in sub-projects. * For more information, see * Applying external plugins with same version to subprojects. */ id("com.android.application") version "7.1.0-beta02" apply false id("com.android.library") version "7.1.0-beta02" apply false id("org.jetbrains.kotlin.android") version "1.5.30" apply false } tasks.register("clean", Delete::class) { delete(rootProject.buildDir) }
配置项目全局属性
对于包含多个模块的 Android 项目,可能有必要在项目级别定义某些属性并在所有模块之间共享这些属性。为此,您可以将额外的属性添加到顶层 build.gradle
文件内的 ext
代码块中。
Groovy
// This block encapsulates custom properties and makes them available to all // modules in the project. The following are only a few examples of the types // of properties you can define. ext { sdkVersion = 28 // You can also create properties to specify versions for dependencies. // Having consistent versions between modules can avoid conflicts with behavior. supportLibVersion = "28.0.0" ... } ...
Kotlin
// This block encapsulates custom properties and makes them available to all // modules in the project. The following are only a few examples of the types // of properties you can define. ext { extra["compileSdkVersion"] = 28 // You can also create properties to specify versions for dependencies. // Having consistent versions between modules can avoid conflicts with behavior. extra["supportLibVersion"] = "28.0.0" ... } ...
如需从同一项目中的模块访问这些属性,请在该模块的 build.gradle
文件(您可以在下一部分中详细了解此文件)中使用以下语法。
Groovy
android { // Use the following syntax to access properties you defined at the project level: // rootProject.ext.property_name compileSdkVersion rootProject.ext.compileSdkVersion ... } ... dependencies { implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}" ... }
Kotlin
android { // Use the following syntax to access properties you defined at the project level: // rootProject.extra["property_name"] compileSdk = rootProject.extra["sdkVersion"] // Alternatively, you can access properties using a type safe delegate: val sdkVersion: Int by rootProject.extra ... compileSdk = sdkVersion } ... dependencies { implementation("com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}") ... }
注意:虽然 Gradle 可让您在模块级别定义项目全局属性,但您应避免这样做,因为这样会导致共享这些属性的模块相互结合。模块结合使得以后将模块作为独立项目导出更加困难,并实际妨碍 Gradle 利用并行项目执行加快多模块构建。
模块级构建文件
模块级 build.gradle
文件位于每个 project/module/
目录下,用于为其所在的特定模块配置构建设置。您可以通过配置这些构建设置提供自定义打包选项(如额外的构建类型和产品变种),以及替换 main/
应用清单或顶层 build.gradle
文件中的设置。
以下 Android 应用模块 build.gradle
文件示例简要说明了您应该了解的一些基础 DSL 元素和设置。
Groovy
/** * The first line in the build configuration applies the Android plugin for * Gradle to this build and makes the android block available to specify * Android-specific build options. */ plugins { id 'com.android.application' } /** * The android block is where you configure all your Android-specific * build options. */ android { /** * compileSdkVersion specifies the Android API level Gradle should use to * compile your app. This means your app can use the API features included in * this API level and lower. */ compileSdkVersion 28 /** * buildToolsVersion specifies the version of the SDK build tools, command-line * utilities, and compiler that Gradle should use to build your app. You need to * download the build tools using the SDK Manager. * * This property is optional because the plugin uses a recommended version of * the build tools by default. */ buildToolsVersion "30.0.2" /** * The defaultConfig block encapsulates default settings and entries for all * build variants, and can override some attributes in main/AndroidManifest.xml * dynamically from the build system. You can configure product flavors to override * these values for different versions of your app. */ defaultConfig { /** * applicationId uniquely identifies the package for publishing. * However, your source code should still reference the package name * defined by the namespace property (for simplicity, keep the * applicationId and namespace the same). */ applicationId 'com.example.myapp' // Defines the minimum API level required to run the app. minSdkVersion 15 // Specifies the API level used to test the app. targetSdkVersion 28 // Defines the version number of your app. versionCode 1 // Defines a user-friendly version name for your app. versionName "1.0" } /** * The buildTypes block is where you can configure multiple build types. * By default, the build system defines two build types: debug and release. The * debug build type is not explicitly shown in the default build configuration, * but it includes debugging tools and is signed with the debug key. The release * build type applies Proguard settings and is not signed by default. */ buildTypes { /** * By default, Android Studio configures the release build type to enable code * shrinking, using minifyEnabled, and specifies the default Proguard rules file. */ release { minifyEnabled true // Enables code shrinking for the release build type. proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } /** * The productFlavors block is where you can configure multiple product flavors. * This allows you to create different versions of your app that can * override the defaultConfig block with their own settings. Product flavors * are optional, and the build system does not create them by default. * * This example creates a free and paid product flavor. Each product flavor * then specifies its own application ID, so that they can exist on the Google * Play Store, or an Android device, simultaneously. * * If you declare product flavors, you must also declare flavor dimensions * and assign each flavor to a flavor dimension. */ flavorDimensions "tier" productFlavors { free { dimension "tier" applicationId 'com.example.myapp.free' } paid { dimension "tier" applicationId 'com.example.myapp.paid' } } } /** * The dependencies block in the module-level build configuration file * specifies dependencies required to build only the module itself. * To learn more, go to Add build dependencies. */ dependencies { implementation project(":lib") implementation 'com.android.support:appcompat-v7:28.0.0' implementation fileTree(dir: 'libs', include: ['*.jar']) }
Kotlin
/** * The first section in the build configuration applies the Android plugin for * Gradle to this build and makes the android block available to specify * Android-specific build options. */ plugins { id("com.android.application") } /** * The android block is where you configure all your Android-specific * build options. */ android { /** * compileSdkVersion specifies the Android API level Gradle should use to * compile your app. This means your app can use the API features included in * this API level and lower. */ compileSdk = 28 /** * buildToolsVersion specifies the version of the SDK build tools, command-line * utilities, and compiler that Gradle should use to build your app. You need to * download the build tools using the SDK Manager. * * This property is optional because the plugin uses a recommended version of * the build tools by default. */ buildToolsVersion = "30.0.2" /** * The defaultConfig block encapsulates default settings and entries for all * build variants, and can override some attributes in main/AndroidManifest.xml * dynamically from the build system. You can configure product flavors to override * these values for different versions of your app. */ defaultConfig { /** * applicationId uniquely identifies the package for publishing. * However, your source code should still reference the package name * defined by the namespace property (for simplicity, keep the * applicationId and namespace the same). */ applicationId = "com.example.myapp" // Defines the minimum API level required to run the app. minSdk = 15 // Specifies the API level used to test the app. targetSdk = 28 // Defines the version number of your app. versionCode = 1 // Defines a user-friendly version name for your app. versionName = "1.0" } /** * The buildTypes block is where you can configure multiple build types. * By default, the build system defines two build types: debug and release. The * debug build type is not explicitly shown in the default build configuration, * but it includes debugging tools and is signed with the debug key. The release * build type applies Proguard settings and is not signed by default. */ buildTypes { /** * By default, Android Studio configures the release build type to enable code * shrinking, using minifyEnabled, and specifies the default Proguard rules file. */ getByName("release") { isMinifyEnabled = true // Enables code shrinking for the release build type. proguardFiles( getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" ) } } /** * The productFlavors block is where you can configure multiple product flavors. * This allows you to create different versions of your app that can * override the defaultConfig block with their own settings. Product flavors * are optional, and the build system does not create them by default. * * This example creates a free and paid product flavor. Each product flavor * then specifies its own application ID, so that they can exist on the Google * Play Store, or an Android device, simultaneously. * * If you declare product flavors, you must also declare flavor dimensions * and assign each flavor to a flavor dimension. */ flavorDimensions = "tier" productFlavors { create("free") { dimension = "tier" applicationId = "com.example.myapp.free" } create("paid") { dimension = "tier" applicationId = "com.example.myapp.paid" } } } /** * The dependencies block in the module-level build configuration file * specifies dependencies required to build only the module itself. * To learn more, go to Add build dependencies. */ dependencies { implementation(project(":lib")) implementation("com.android.support:appcompat-v7:28.0.0") implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar")))) }
Gradle 属性文件
Gradle 还包含两个属性文件,它们位于项目的根目录下,可用于指定 Gradle 构建工具包本身的设置:
-
gradle.properties
- 您可以在其中配置项目全局 Gradle 设置,如 Gradle 守护程序的最大堆大小。如需了解详情,请参阅构建环境。
-
local.properties
-
为构建系统配置本地环境属性,其中包括:
ndk.dir
- NDK 的路径。此属性已被弃用。NDK 的所有下载版本都将安装在 Android SDK 目录下的ndk
目录中。sdk.dir
- SDK 的路径。cmake.dir
- CMake 的路径。ndk.symlinkdir
- 在 Android Studio 3.5 及更高版本中,创建指向 NDK 的符号链接,该符号链接的路径可比 NDK 安装路径短。
将 NDK 重新映射到较短的路径(仅限 Windows)
Windows 长路径最常见的问题就是 NDK 安装文件夹中的工具(如 ld.exe
)会产生非常深的路径,但工具对于长路径的支持并不佳。
在 local.properties
中,您可以设置 ndk.symlinkdir
属性以请求 Gradle 插件创建指向 NDK 的符号链接。该符号链接的路径可比现有 NDK 文件夹的路径短。例如,ndk.symlinkdir = C:\
将生成以下符号链接:C:\ndk\19.0.5232133
将项目与 Gradle 文件同步
当您在项目中对构建配置文件进行更改时,Android Studio 会要求您同步项目文件,以便它导入构建配置更改并执行一些检查以确保您的配置不会造成构建错误。
如需同步项目文件,请点击做出更改后显示的通知栏中的 Sync Now(如图 3 所示),或者点击菜单栏中的 Sync Project 图标 。如果 Android Studio 发现您的配置有任何错误(例如,您的源代码使用了只有在
compileSdkVersion
以上的 API 级别中才会提供的 API 功能),就会显示 Messages 窗口,以说明相应的问题。
图 3. 在 Android Studio 中将项目与构建配置文件同步。
源代码集
Android Studio 按逻辑关系将每个模块的源代码和资源分组为源代码集。模块的 main/
源代码集包含其所有构建变体共用的代码和资源。其他源代码集目录是可选的,在您配置新的构建变体时,Android Studio 不会自动为您创建这些目录。不过,创建类似于 main/
的源代码集有助于组织 Gradle 仅在构建特定应用版本时才应使用的文件和资源:
-
src/main/
- 此源代码集包含所有构建变体共用的代码和资源。
-
src/buildType/
- 创建此源代码集可加入特定构建变体专用的代码和资源。
-
src/productFlavor/
-
创建此源代码集可加入特定产品变种专用的代码和资源。
注意:如果配置构建以组合多个产品变种,则可以为变种维度之间的每个产品变种组合创建源代码集目录:
src/productFlavor1ProductFlavor2/
-
src/productFlavorBuildType/
- 创建此源代码集可加入特定构建变体专用的代码和资源。
例如,如需生成应用的“fullDebug”版本,构建系统需要合并来自以下源代码集的代码、设置和资源:
-
src/fullDebug/
(构建变体源代码集) -
src/debug/
(构建类型源代码集) -
src/full/
(产品变种源代码集) -
src/main/
(主源代码集)
注意:当您在 Android Studio 中使用 File > New 菜单选项新建文件或目录时,可以针对特定源代码集进行创建。可供您选择的源代码集取决于您的构建配置,如果所需的目录尚不存在,Android Studio 会自动创建。
如果不同源代码集包含同一文件的不同版本,Gradle 将按以下优先顺序决定使用哪一个文件(左侧源代码集替换右侧源代码集的文件和设置):
构建变体 > 构建类型 > 产品变种 > 主源代码集 > 库依赖项
这样一来,Gradle 便可使用专用于您试图构建的构建变体的文件,同时重复利用与应用的其他版本共用的 Activity、应用逻辑和资源。在合并多个清单时,Gradle 会使用相同的优先顺序,这样每个构建变体都能在最终清单中定义不同的组件或权限。如需详细了解如何创建自定义源代码集,请参阅创建构建变体的源代码集。