您可以使用 Gradle 封装容器命令行工具
执行 Android 项目可用的所有构建任务。它可作为 Windows 的批处理文件 (gradlew.bat)
和 Linux 与 Mac 的 shell 脚本 (gradlew.sh)
使用,而且您可以从使用 Android Studio 创建的每个项目的根目录启动该工具。
如需使用封装容器运行任务,请在终端窗口中(在 Android Studio 中,依次选择 View > Tool Windows > Terminal
)使用下列命令之一:
gradlew task-name
./gradlew task-name
gradlew tasks
如需立即测试和调试应用,您可以构建调试版 APK。调试版 APK 使用 SDK 工具提供的调试密钥进行签名,并允许通过 adb 进行调试。
如需构建调试版 APK,请打开命令行,然后转到项目的根目录。如需启动调试 build,请调用 assembleDebug
任务:
gradlew assembleDebug
这将在 project_name/module_name/build/outputs/apk/
中创建一个名为 module_name-debug.apk
的 APK
。该文件已使用调试密钥进行签名并使用 zipalign
对齐,因此您可以立即将其安装到设备上。
或者,如需构建 APK 并立即在运行的模拟器或连接的设备上安装,请改为调用 installDebug
:
gradlew installDebug
上述任务名称中的“Debug”部分只是构建变体名称的驼峰式大小写版本,因此可以替换为您想组装或安装的任何构建类型或变体。例如,如果您有“demo”这个产品变种,则可以使用 assembleDemoDebug 任务来构建调试版本
。
打开模块级 build.gradle 文件,并添加包含 storeFile、storePassword、keyAlias 和 keyPassword 条目的 signingConfigs {} 代码块,然后将该对象传递给您的版本类型中的 signingConfig 属性。例如:
android {
...
defaultConfig { ... }
signingConfigs {
release {
// You need to specify either an absolute path or include the
// keystore file in the same directory as the build.gradle file.
storeFile file("my-release-key.jks")
storePassword "password"
keyAlias "my-alias"
keyPassword "password"
}
}
buildTypes {
release {
signingConfig signingConfigs.release
...
}
}
}
创建自定义构建配置需要您对一个或多个构建配置文件(即 build.gradle 文件
)做出更改。这些纯文本文件使用领域特定语言 (DSL) 以 Groovy
描述和操纵构建逻辑,其中 Groovy 是一种适用于 Java 虚拟机 (JVM) 的动态语言
。您无需了解 Groovy 便可开始配置构建,因为 Android Plugin for Gradle 引入了您需要的大多数 DSL 元素。如需详细了解 Android 插件 DSL,请参阅 DSL 参考文档。
开始新项目时,Android Studio
会自动为您创建其中的部分文件(如下图所示),并为其填充合理的默认值,Android 应用模块的默认项目结构
settings.gradle
文件位于项目的根目录下,用于指示 Gradle 在构建应用时应将哪些模块包含在内。对大多数项目而言,该文件很简单,只包含以下内容:
include ‘:app’
不过,多模块项目需要指定应包含在最终构建中的每个模块。
顶层 build.gradle
文件位于项目的根目录下,用于定义适用于项目中所有模块的构建配置。默认情况下,顶层构建文件使用 buildscript
代码块定义项目中所有模块共用的 Gradle 代码库和依赖项。以下代码示例说明了创建新项目后可在顶层 build.gradle 文件中找到的默认设置和 DSL 元素。
/**
* The buildscript block is where you configure the repositories and
* dependencies for Gradle itself—meaning, you should not include dependencies
* for your modules here. For example, this block includes the Android plugin for
* Gradle as a dependency because it provides the additional instructions Gradle
* needs to build Android app modules.
*/
buildscript {
/**
* The repositories block configures the repositories Gradle uses to
* search or download the 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
* JCenter as the repository Gradle should use to look for its dependencies.
*
* New projects created using Android Studio 3.0 and higher also include
* Google's Maven repository.
*/
repositories {
google()
jcenter()
}
/**
* The dependencies block configures the dependencies Gradle needs to use
* to build your project. The following line adds Android plugin for Gradle
* version 4.0.0 as a classpath dependency.
*/
dependencies {
classpath 'com.android.tools.build:gradle:4.0.0'
}
}
/**
* The allprojects block is where you configure the repositories and
* dependencies used by all modules in your project, such as third-party plugins
* or libraries. However, you should configure module-specific dependencies in
* each module-level build.gradle file. For new projects, Android Studio
* includes JCenter and Google's Maven repository by default, but it does not
* configure any dependencies (unless you select a template that requires some).
*/
allprojects {
repositories {
google()
jcenter()
}
}
对于包含多个模块的 Android 项目,可能有必要在项目级别定义某些属性并在所有模块之间共享这些属性
。为此,您可以将额外的属性
添加到顶层 build.gradle 文件内的 ext 代码块
中。
buildscript {...}
allprojects {...}
// This block encapsulates custom properties and makes them available to all
// modules in the project.
ext {
// The following are only a few examples of the types of properties you can define.
compileSdkVersion = 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"
...
}
...
如需从同一项目中的模块访问这些属性
,请在该模块的 build.gradle 文件(您可以在下一部分中详细了解此文件)中使用以下语法。
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}"
...
}
模块级 build.gradle
文件位于每个 project/module/ 目录
下,用于为其所在的特定模块配置构建设置。您可以通过配置这些构建设置提供自定义打包选项(如额外的构建类型和产品变种),以及替换 main/ 应用清单或顶层 build.gradle 文件中的设置。
以下 Android 应用模块 build.gradle 文件示例简要说明了您应该了解的一些基础 DSL 元素和设置。
/**
* 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.
*/
apply plugin: '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 "29.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 package attribute in the main/AndroidManifest.xml file.
*/
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 splits block is where you can configure different APK builds that
* each contain only code and resources for a supported screen density or
* ABI. You'll also need to configure your build so that each APK has a
* different versionCode.
*/
splits {
// Settings to build multiple APKs based on screen density.
density {
// Enable or disable building multiple APKs.
enable false
// Exclude these densities when building multiple APKs.
exclude "ldpi", "tvdpi", "xxxhdpi", "400dpi", "560dpi"
}
}
}
/**
* 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'])
}
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 安装路径短。local.properties
中,您可以设置 ndk.symlinkdir
属性以请求 Gradle
插件创建指向 NDK 的符号链接。该符号链接的路径可比现有 NDK 文件夹的路径短。例如,ndk.symlinkdir = C:\ 将生成以下符号链接:C:\ndk\19.0.5232133
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 会使用相同的优先顺序,这样每个构建变体都能在最终清单中定义不同的组件或权限。如需详细了解如何创建自定义源代码集,请参阅创建构建变体的源代码集。
每个 Android 应用均有一个唯一的应用 ID,像 Java 软件包名称一样,例如 com.example.myapp
。此 ID 可以在设备上和 Google Play 商店中对您的应用进行唯一标识。如果您要上传新版本的应用,应用 ID(以及用于为其签名的证书
)必须与原始 APK 相同。如果您更改了应用 ID,Google Play 商店会将该 APK 视为完全不同的应用。因此,发布您的应用后,绝不应更改应用 ID
。
您的应用 ID 通过模块的 build.gradle
文件中的 applicationId
属性定义,如下所示:
android {
defaultConfig {
applicationId "com.example.myapp"
minSdkVersion 15
targetSdkVersion 24
versionCode 1
versionName "1.0"
}
...
}
当您在 Android Studio 中创建新项目时,applicationId
与您在设置期间选择的 Java 样式软件包名称完全一致。不过,除了这一点,应用 ID 和软件包名称彼此无关。您可以更改代码的软件包名称(代码命名空间),这不会影响应用 ID,反之亦然(但同样,发布您的应用后,不得更改应用 ID)。不过,更改软件包名称会产生其他影响,您应了解这一点,因此请参阅有关如何更改软件包名称的部分
。
虽然应用 ID 看起来就像传统的 Java 软件包名称一样,但应用 ID 的命名规则限制性更强一些:
必须至少包含两段(一个或多个圆点)。
每段必须以字母开头。
所有字符必须为字母数字或下划线 [a-zA-Z0-9_]
注意:应用 ID 过去直接关联到代码的软件包名称;所以,有些 Android API 会在其方法名称和参数名称中使用“package name”一词,但这实际上是您的应用 ID。例如,Context.getPackageName() 方法会返回您的应用 ID。无论何时都不需要在应用代码以外分享代码的真实软件包名称。
当您为应用构建 APK 时,构建工具会使用 build.gradle
文件的 defaultConfig
块中定义的应用 ID 标记 APK(如下所示)。不过,如果您要创建不同版本的应用,让其在 Google Play 商店中显示为单独的详情(如“free”和“pro”版本),就需要创建单独的构建变体,这些变体各自具有不同的应用 ID。
在这种情况下,每个构建变体应定义为单独的产品变种。对于 productFlavors
块中的每个变种,您可以重新定义 applicationId
属性,也可以使用 applicationIdSuffix
在默认的应用 ID 上追加一段,如下所示:
android {
defaultConfig {
applicationId "com.example.myapp"
}
productFlavors {
free {
applicationIdSuffix ".free"
}
pro {
applicationIdSuffix ".pro"
}
}
}
这样,“free”产品变种的应用 ID 就是“com.example.myapp.free”
。
您也可以根据自己的构建类型使用 applicationIdSuffix
追加一段,如下所示:
android {
...
buildTypes {
debug {
applicationIdSuffix ".debug"
}
}
}
由于 Gradle 会在产品变种后面应用构建类型配置,因此“free debug”
构建变体的应用 ID 现在是“com.example.myapp.free.debug”
。如果您希望同一设备上同时具有调试版本和发布版本,这会很有用,因为两个 APK 不能具有相同的应用 ID。
请注意,具有不同应用 ID 的 APK 在 Google Play 商店中会被视为不同的应用。所以如果您想要改用相同的应用详情分发多个 APK,每个 APK 以不同设备配置(如 API 级别)为目标,那么您必须对每个构建变体使用相同的应用 ID,但为每个 APK 提供不同的 versionCode
。如需了解详情,请参阅多 APK 支持。
注意:为了与以前的 SDK 工具兼容,如果您未在build.gradle 文件中定义 applicationId 属性,构建工具会将
AndroidManifest.xml 文件中的软件包名称用作应用 ID。在这种情况下,重构您的软件包名称也会更改您的应用 ID。
提示:如果需要在清单文件中引用应用 ID,您可以在任何清单属性中使用 ${applicationId} 占位符。在构建期间,Gradle 会将此标记替换为实际的应用 ID。如需了解详情,请参阅将构建变量注入清单。
默认情况下,构建工具会将应用 ID 应用到您的插桩测试 APK,该 APK 将应用 ID 用于给定的构建变体,同时追加 .test。例如,com.example.myapp.free
构建变体的测试 APK 的应用 ID 为 com.example.myapp.free.test
。
您可以通过在 defaultConfig
或 productFlavor
块中定义 testApplicationId
属性来更改应用 ID,不过应该没有必要这样做。
注意:为了避免与受测应用发生名称冲突,构建工具会为您的测试 APK 生成 R 类,其命名空间基于测试应用
ID,而不是清单文件中定义的软件包名称。
默认情况下,项目的软件包名称与应用 ID 匹配,但您可以更改软件包名称。不过,如果您要更改软件包名称,需要注意的是,软件包名称(由项目目录结构定义)应始终与 AndroidManifest.xml 文件中的 package 属性匹配
,如下所示:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp"
android:versionCode="1"
android:versionName="1.0" >
Android 构建工具使用 package 属性来发挥两种作用:
它将此名称用作应用生成的 R.java 类的命名空间。示例:对于上面的清单,R 类将为 com.example.myapp.R。
它会使用此名称解析清单文件中声明的任何相关类名。
示例:对于上面的清单,声明为
的 Activity 将解析为
com.example.myapp.MainActivity。
因此,package 属性中的名称应始终与项目的基础软件包名称匹配
,基础软件包中保存着您的 Activity 及其他应用代码。当然,您的项目中可以包含子软件包,但是这些文件必须从 package
属性导入使用命名空间的 R.java
类,而且清单中声明的任何应用组件都必须添加缺失的子软件包名称(或者使用完全限定软件包名称)。
如果您要完全重构您的软件包名称,请确保也更新 package 属性。只要您使用 Android Studio 的工具重命名和重构您的软件包,那么这些属性就会自动保持同步。(如果它们未保持同步,您的应用代码将无法解析 R 类,因为它不再位于同一软件包中,并且清单无法识别您的 Activity 或其他组件。)
您必须始终在项目的主 AndroidManifest.xml 文件中指定 package 属性
。如果您有其他清单文件(如产品变种或构建类型的清单文件),请注意,优先级最高的清单文件提供的软件包名称始终用于最终合并的清单。如需了解详情,请参阅合并多个清单文件。
还有一点需要了解:尽管清单 package 和 Gradle applicationId 可以具有不同的名称,但构建工具会在构建结束时将应用ID 复制到 APK 的最终清单文件中。所以,如果您在构建后检查 AndroidManifest.xml 文件,发现 package
属性发生更改就不足为奇了。实际上,Google Play 商店和 Android 平台会查看 package属性来识别您的应用。所以,构建系统利用原始值(设置 R 类的命名空间并解析清单类名称)后,它会舍弃该值并将其替换为应用 ID。
利用 Android Studio
中的 Gradle
构建系统,您可以轻松地将外部二进制文件或其他库模块作为依赖项添加到您的构建中。这些依赖项可位于您的计算机上或远程代码库中,并且它们声明的所有传递依赖项也会自动包含在内。本页介绍了如何在您的 Android 项目中使用依赖项,包括有关 Android Plugin for Gradle 特有的行为和配置的详细信息。如需更深入地了解 Gradle 依赖项的概念,您还应该参阅 Gradle 依赖项管理指南。但请注意,您的 Android 项目只能使用本页上定义的依赖项配置。
注意:指定依赖项时,不应使用动态版本号,如
‘com.android.tools.build:gradle:3.+’。使用此功能可能会导致意外的版本更新和难以解析版本差异。
如需向您的项目添加依赖项,请在 build.gradle
文件的 dependencies
代码块中指定依赖项配置,如 implementation
。
例如,应用模块的以下 build.gradle
文件包含三种不同类型的依赖项:
apply plugin: 'com.android.application'
android { ... }
dependencies {
// Dependency on a local library module
implementation project(":mylibrary")
// Dependency on local binaries
implementation fileTree(dir: 'libs', include: ['*.jar'])
// Dependency on a remote binary
implementation 'com.example.android:app-magic:12.3'
}
其中每种依赖项配置都请求不同种类的库依赖项,如下所示:
本地库模块依赖项
implementation project(':mylibrary')
这声明了对一个名为“mylibrary”
(此名称必须与在您的 settings.gradle
文件中使用 include:
定义的库名称相符)的 Android 库模块的依赖关系。在构建您的应用时,构建系统会编译该库模块,并将生成的编译内容打包到 APK 中。
本地二进制文件依赖项
implementation fileTree(dir: 'libs', include: ['*.jar'])
Gradle
声明了对项目的 module_name/libs/
目录中 JAR 文件的依赖关系(因为 Gradle
会读取 build.gradle
文件的相对路径)。
或者,您也可以按如下方式指定各个文件:
implementation files('libs/foo.jar', 'libs/bar.jar')
远程二进制文件依赖项
implementation 'com.example.android:app-magic:12.3'
这实际上是以下代码的简写形式:
implementation group: 'com.example.android', name: 'app-magic', version: '12.3'
这声明了对“com.example.android”
命名空间组内的 12.3 版“app-magic”
库的依赖关系。
注意:此类远程依赖项要求您声明 Gradle 应在其中查找库的相应远程代码库。如果本地不存在相应的库,那么当 build 需要它时(例如,当您点击 Sync Project with Gradle Files 图标 或运行 build 时),Gradle会从远程站点提取它。
在 dependencies
代码块内,您可以从多种不同的依赖项配置中选择其一(如上面所示的 implementation
)来声明库依赖项。每种依赖项配置都向 Gradle
提供了有关如何使用该依赖项的不同说明。下表介绍了您可以对 Android 项目中的依赖项使用的各种配置。此表还将这些配置与自 Android Gradle 插件 3.0.0 起弃用的配置进行了比较
。
配置 | 行为 |
---|---|
implementation | Gradle 会将依赖项添加到编译类路径,并将依赖项打包到构建输出 。不过,当您的模块配置 implementation 依赖项时,会让 Gradle 了解您不希望该模块在编译时将该依赖项泄露给其他模块 。也就是说,其他模块只有在运行时才能使用该依赖项 。使用此依赖项配置代替 api 或 compile(已弃用)可以显著缩短构建时间,因为这样可以减少构建系统需要重新编译的模块数。例如,如果 implementation 依赖项更改了其 API,Gradle 只会重新编译该依赖项以及直接依赖于它的模块。大多数应用和测试模块都应使用此配置。 |
api | Gradle 会将依赖项添加到编译类路径和构建输出 。当一个模块包含 api 依赖项时,会让 Gradle 了解该模块要以传递方式将该依赖项导出到其他模块,以便这些模块在运行时和编译时都可以使用该依赖项 。此配置的行为类似于 compile(现已弃用) ,但使用它时应格外小心,只能对您需要以传递方式导出到其他上游消费者的依赖项使用它。这是因为,如果 api 依赖项更改了其外部 API,Gradle 会在编译时重新编译所有有权访问该依赖项的模块。因此,拥有大量的 api 依赖项会显著增加构建时间。除非要将依赖项的 API 公开给单独的模块,否则库模块应改用 implementation 依赖项。 |
compileOnly | Gradle 只会将依赖项添加到编译类路径(也就是说,不会将其添加到构建输出) 。如果您创建 Android 模块时在编译期间需要相应依赖项,但它在运行时可有可无,此配置会很有用。如果您使用此配置,那么您的库模块必须包含一个运行时条件,用于检查是否提供了相应依赖项,然后适当地改变该模块的行为,以使该模块在未提供相应依赖项的情况下仍可正常运行 。这样做不会添加不重要的瞬时依赖项,因而有助于减小最终 APK 的大小。此配置的行为类似于 provided(现已弃用) 。注意:您不能将 compileOnly 配置与 AAR 依赖项配合使用 ,因为aar已经是编译过的了 |
runtimeOnly | Gradle 只会将依赖项添加到构建输出,以便在运行时使用。也就是说,不会将其添加到编译类路径。此配置的行为类似于 apk(现已弃用)。 |
annotationProcessor | 如需添加对作为注解处理器的库的依赖,您必须使用 annotationProcessor 配置将其添加到注解处理器的类路径。这是因为,使用此配置可以将编译类路径与注释处理器类路径分开,从而提高构建性能 。如果 Gradle 在编译类路径上找到注释处理器,则会禁用避免编译功能,这样会对构建时间产生负面影响(Gradle 5.0 及更高版本会忽略在编译类路径上找到的注释处理器)。如果 JAR 文件包含以下文件,则 Android Gradle 插件会假定依赖项是注释处理器:META-INF/services/javax.annotation.processing.Processor。 如果插件检测到编译类路径上包含注解处理器,则会产生构建错误。`注意:Kotlin 项目应使用 kapt 声明注解处理器依赖项。 |
lintChecks | 使用此配置可以添加您希望 Gradle 在构建项目时执行的 lint 检查。注意:使用 Android Gradle 插件 3.4.0 及更高版本时,此依赖项配置不再将 lint 检查打包在 Android 库项目中。如需将 lint 检查依赖项包含在 AAR 库中,请使用下面介绍的 lintPublish 配置。 |
lintPublish | 在 Android 库项目中使用此配置可以添加您希望 Gradle 编译成 lint.jar 文件并打包在 AAR 中的 lint 检查。这会使得使用 AAR 的项目也应用这些 lint 检查。如果您之前使用 lintChecks 依赖项配置将 lint 检查添加到已发布的 AAR 中,则需要迁移这些依赖项以改用 lintPublish 配置。dependencies { // Executes lint checks from the ‘:checks’ project at build time.lintChecks project(':checks') // Compiles lint checks from the ‘:checks-to-publish’ into a lint.jar file and publishes it to your Android library. lintPublish project(':checks-to-publish') } |
配置 | 行为 |
---|---|
apk | Gradle 只会将依赖项添加到构建输出,以便在运行时使用。也就是说,不会将其添加到编译类路径。 |
compile | Gradle 会将依赖项添加到编译类路径和构建输出。 将依赖项导出到其他模块。 |
provided | Gradle 只会将依赖项添加到编译类路径(也就是说,不会将其添加到构建输出)。 |
以上所有配置会将依赖项应用于所有构建变体。如果您只想为特定的构建变体
源代码集或测试源代码集
声明依赖项,则必须将配置名称的首字母大写,并在其前面加上构建变体或测试源代码集的名称作为前缀。
例如,如需只向“free”产品变种添加 implementation
依赖项(使用远程二进制文件依赖项),请使用如下所示的代码:
dependencies {
freeImplementation 'com.google.firebase:firebase-ads:9.8.0'
}
不过,如果您想为将产品变种和构建类型组合在一起的变体添加依赖项,就必须在 configurations
代码块中初始化配置名称。以下示例向“freeDebug”
构建变体添加了 runtimeOnly
依赖项(使用本地二进制文件依赖项):
configurations {
// Initializes a placeholder for the freeDebugRuntimeOnly dependency
// configuration.
freeDebugRuntimeOnly {}
}
dependencies {
freeDebugRuntimeOnly fileTree(dir: 'libs', include: ['*.jar'])
}
如需为本地测试和插桩测试添加 implementation
依赖项,请使用如下所示的代码:
dependencies {
// Adds a remote binary dependency only for local tests.
testImplementation 'junit:junit:4.12'
// Adds a remote binary dependency only for the instrumented test APK.
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
不过,某些配置在这种情况下没有意义。例如,因为其他模块不能依赖于 androidTest
,所以如果您使用 androidTestApi
配置,会收到以下警告:
WARNING: Configuration 'androidTestApi' is obsolete and has been replaced with
'androidTestImplementation'.
如果将注释处理器添加到编译类路径,您将看到一条与以下内容类似的错误消息:
Error: Annotation processors must be explicitly declared now.
如需解决此错误,请使用 annotationProcessor
配置依赖项,以将注解处理器添加到您的项目,如下所示:
dependencies {
// Adds libraries defining annotations to only the compile classpath.
compileOnly 'com.google.dagger:dagger:version-number'
// Adds the annotation processor dependency to the annotation processor classpath.
annotationProcessor 'com.google.dagger:dagger-compiler:version-number'
}
注意:Android Plugin for Gradle 3.0.0 及更高版本不再支持 android-apt 插件。
如果需要向注解处理器传递参数,您可以使用模块构建配置中的 AnnotationProcessorOptions
代码块执行此操作。例如,如果要以键值对形式传递基元数据类型,您可以使用 argument
属性,如下所示:
android {
...
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
argument "key1", "value1"
argument "key2", "value2"
}
}
}
}
不过,在使用 Android Gradle 插件 3.2.0 及更高版本时,您需要使用 Gradle 的 CommandLineArgumentProvider 接口传递表示文件或目录的处理器参数
。
借助 CommandLineArgumentProvider
,您或注解处理器创建者可将增量构建属性类型注解应用于每个参数,从而提高增量构建和缓存整洁构建的正确性和性能。
例如,下面的类实现了 CommandLineArgumentProvider
并注释了处理器的每个参数。此外,此示例还使用了 Groovy 语言语法,并且直接包含在模块的 build.gradle 文件中。
注意:通常,注释处理器作者会提供此类或有关如何编写这种类的说明。这是因为,每个参数都需要指定正确的构建属性类型注解,才能按预期运行。
class MyArgsProvider implements CommandLineArgumentProvider {
// Annotates each directory as either an input or output for the
// annotation processor.
@InputFiles
// Using this annotation helps Gradle determine which part of the file path
// should be considered during up-to-date checks.
@PathSensitive(PathSensitivity.RELATIVE)
FileCollection inputDir
@OutputDirectory
File outputDir
// The class constructor sets the paths for the input and output directories.
MyArgsProvider(FileCollection input, File output) {
inputDir = input
outputDir = output
}
// Specifies each directory as a command line argument for the processor.
// The Android plugin uses this method to pass the arguments to the
// annotation processor.
@Override
Iterable<String> asArguments() {
// Use the form '-Akey[=value]' to pass your options to the Java compiler.
["-AinputDir=${inputDir.singleFile.absolutePath}",
"-AoutputDir=${outputDir.absolutePath}"]
}
}
android {...}
创建一个实现 CommandLineArgumentProvider
的类后,您需要对其进行初始化并使用 annotationProcessorOptions.compilerArgumentProvider
属性将其传递给 Android 插件,如下所示。
// This is in your module's build.gradle file.
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
// Creates a new MyArgsProvider object, specifies the input and
// output paths for the constructor, and passes the object
// to the Android plugin.
compilerArgumentProvider new MyArgsProvider(files("input/path"),
new File("output/path"))
}
}
}
}
如需详细了解实现 CommandLineArgumentProvider
如何帮助提高构建性能,请阅读缓存 Java 项目。
如果编译类路径中的依赖项包含您不需要的注释处理器,您可以通过将以下代码添加到 build.gradle
文件来停用错误检查。请注意,您添加到编译类路径中的注释处理器仍不会被添加到处理器类路径中
。
android {
...
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
includeCompileClasspath false
}
}
}
}
如果在将项目的注释处理器迁移到处理器类路径后遇到问题,您可以通过将 includeCompileClasspath
设为 true,允许编译类路径中包含注释处理器。不过,不建议将此属性设为 true,在 Android 插件的未来更新中将会移除用来执行此操作的选项。
随着应用的范围不断扩大,它可能会包含许多依赖项,包括直接依赖项和传递依赖项(应用中导入的库所依赖的库)。如需排除不再需要的传递依赖项,您可以使用 exclude
关键字,如下所示:
dependencies {
implementation('some-library') {
exclude group: 'com.example.imgtools', module: 'native'
}
}
如果您需要从测试中排除某些传递依赖项,上面所示的代码示例可能无法按预期发挥作用。这是因为,测试配置(例如 androidTestImplementation
)扩展了模块的 implementation
配置。也就是说,当 Gradle
解析配置时,测试配置始终包含 implementation
依赖项。
因此,如需从测试中排除传递依赖项,则必须在执行代码时执行此操作,如下所示:
android.testVariants.all { variant ->
variant.getCompileConfiguration().exclude group: 'com.jakewharton.threetenabp', module: 'threetenabp'
variant.getRuntimeConfiguration().exclude group: 'com.jakewharton.threetenabp', module: 'threetenabp'
}
注意:您仍可在依赖项代码块中使用 exclude 关键字(如排除依赖项部分的原始代码示例所示),以省略测试配置特有的(即其他配置不包含的)传递依赖项。
Android 插件 3.0.0 及更高版本包含一种新的依赖项机制
,该机制可在使用库时自动匹配变体
。这意味着,应用的 debug
变体会自动使用库的 debug
变体,依此类推。在使用变种时(这时应用的 freeDebug 变体
将使用库的 freeDebug 变体
),这种机制也同样适用。
为了让插件准确匹配变体,您需要在无法进行直接匹配的情况下提供匹配回退机制
。不妨假设您的应用配置了一个名为“staging”的构建类型,但该应用的一个库依赖项没有进行相应配置。当插件尝试构建“staging”版本的应用时,它不知道要使用哪个版本的库,因此您将看到一条与以下内容类似的错误消息:
Error:Failed to resolve: Could not resolve project :mylibrary.
Required by:
project :app
插件包含一些 DSL 元素,这些元素有助于控制 Gradle 应如何解决应用与依赖项之间无法进行直接变体匹配的问题。请参阅下表,以确定应使用哪个 DSL 属性来解决与变体感知依赖项匹配相关的特定编译错误。
编译错误原因 | 解决方案 |
---|---|
您的应用包含库依赖项不包含的构建类型。例如,您的应用包含“staging”版本类型,但依赖项仅包含“debug”和“release”版本类型。请注意,如果库依赖项包含您的应用不包含的编译类型,这不会引发问题。这是因为,插件在任何时候都不会从依赖项请求该构建类型。 | 使用 matchingFallbacks 为给定的构建类型指定替代匹配,如下所示: |
// In the app's build.gradle file.
android {
buildTypes {
debug {}
release {}
staging {
// Specifies a sorted list of fallback build types that the
// plugin should try to use when a dependency does not include a
// "staging" build type. You may specify as many fallbacks as you
// like, and the plugin selects the first build type that's
// available in the dependency.
matchingFallbacks = ['debug', 'qa', 'release']
}
}
}
编译错误原因 | 解决方案 |
---|---|
对于应用及其库依赖项中均存在的给定变种维度,您的应用包含库不包含的变种 。例如,您的应用及其库依赖项都包含“tier”变种维度。不过,应用中的“tier”维度包含“free”和“paid”变种,但依赖项中的同一维度仅包含“demo”和“paid”变种。请注意,对于应用及其库依赖项中均存在的给定变种维度,如果库包含您的应用不包含的产品变种,这不会引发问题。这是因为,插件在任何时候都不会从依赖项请求该变种。 |
使用 matchingFallbacks 为应用的“free”产品变种指定替代匹配,如下所示: |
// In the app's build.gradle file.
android {
defaultConfig{
// Do not configure matchingFallbacks in the defaultConfig block.
// Instead, you must specify fallbacks for a given product flavor in the
// productFlavors block, as shown below.
}
flavorDimensions 'tier'
productFlavors {
paid {
dimension 'tier'
// Because the dependency already includes a "paid" flavor in its
// "tier" dimension, you don't need to provide a list of fallbacks
// for the "paid" flavor.
}
free {
dimension 'tier'
// Specifies a sorted list of fallback flavors that the plugin
// should try to use when a dependency's matching dimension does
// not include a "free" flavor. You may specify as many
// fallbacks as you like, and the plugin selects the first flavor
// that's available in the dependency's "tier" dimension.
matchingFallbacks = ['demo', 'trial']
}
}
}
编译错误原因 | 解决方案 |
---|---|
库依赖项包含您的应用不包含的变种维度。例如,库依赖项包含“minApi”维度的变种,但您的应用仅包含“tier”维度的变种。因此,当您要构建“freeDebug”版本的应用时,插件不知道是使用“minApi23Debug”还是“minApi18Debug”版本的依赖项。请注意,如果您的应用包含库依赖项不包含的变种维度,这不会引发问题。这是因为,插件只会匹配依赖项中存在的维度的变种。例如,如果依赖项不包含 ABI 的维度,“freeX86Debug”版本的应用将直接使用“freeDebug”版本的依赖项。 | 在 defaultConfig 代码块中使用 missingDimensionStrategy 指定插件应从每个缺失维度中选择的默认变种,如以下示例所示。您也可以替换在 productFlavors 代码块中的选择,让每一个变种都可以为缺失维度指定一个不同的匹配策略。 |
// In the app's build.gradle file.
android {
defaultConfig{
// Specifies a sorted list of flavors that the plugin should try to use from
// a given dimension. The following tells the plugin that, when encountering
// a dependency that includes a "minApi" dimension, it should select the
// "minApi18" flavor. You can include additional flavor names to provide a
// sorted list of fallbacks for the dimension.
missingDimensionStrategy 'minApi', 'minApi18', 'minApi23'
// You should specify a missingDimensionStrategy property for each
// dimension that exists in a local dependency but not in your app.
missingDimensionStrategy 'abi', 'x86', 'arm64'
}
flavorDimensions 'tier'
productFlavors {
free {
dimension 'tier'
// You can override the default selection at the product flavor
// level by configuring another missingDimensionStrategy property
// for the "minApi" dimension.
missingDimensionStrategy 'minApi', 'minApi23', 'minApi18'
}
paid {}
}
}
当您的依赖项不是本地库或文件树时,Gradle 会在 build.gradle
文件的 repositories
代码块中指定的所有在线代码库中查找相关文件。各个代码库的列出顺序决定了 Gradle 在这些代码库中搜索各个项目依赖项的顺序。例如,如果从代码库 A 和 B 均可获得某个依赖项,而您先列出了代码库 A,则 Gradle 会从代码库 A 下载该依赖项。
默认情况下,新的 Android Studio 项目会将 Google 的 Maven 代码库和 JCenter
指定为项目的顶级 build.gradle 文件中的代码库位置,如下所示:
allprojects {
repositories {
google()
jcenter()
}
}
如果您要从 Maven 中央代码库获取某些内容,则添加 mavenCentral()
;对于本地代码库,则使用 mavenLocal()
:
allprojects {
repositories {
google()
jcenter()
mavenCentral()
mavenLocal()
}
}
或者,您也可以按如下方式声明特定的 Maven 或 Ivy 代码库:
allprojects {
repositories {
maven {
url "https://repo.example.com/maven2"
}
maven {
url "file://local/repo/"
}
ivy {
url "https://repo.example.com/ivy"
}
}
}
要了解详情,请参阅 Gradle 代码库指南。
Google 的 Maven 代码库中提供了以下 Android 代码库的最新版本:
Android 支持库
架构组件库
约束布局库
AndroidX 测试
数据绑定库
Android 免安装应用库
Wear OS
Google Play 服务
Google Play 结算库
Firebase
您可以在 Google 的 Maven 代码库索引
中查看所有可用的工件(如需了解以编程方式访问
,请参阅下文)。
如需将其中某个库添加到您的 build 中,请在顶级 build.gradle
文件中包含 Google 的 Maven 代码库:
allprojects {
repositories {
google()
// If you're using a version of Gradle lower than 4.1, you must instead use:
// maven {
// url 'https://maven.google.com'
// }
// An alternative URL is 'https://dl.google.com/dl/android/maven2/'
}
}
然后,将所需的库添加到模块的 dependencies
代码块中。例如,appcompat 库
如下所示:
dependencies {
implementation 'com.android.support:appcompat-v7:28.0.0'
}
不过,如果您在尝试使用旧版上述库时依赖项失败,则表明 Maven 代码库中未提供该版本,您必须从离线代码库
获取该库。
如需以编程方式访问 Google 的 Maven 工件,可以从 maven.google.com/master-index.xml
获取工件组的 XML 列表。然后,您可以从以下位置查看任意组的库名称和版本信息:
maven.google.com/group_path/group-index.xml
例如,android.arch.lifecycle
组中的库就列在 maven.google.com/android/arch/lifecycle/group-index.xml
中。
您也可以从以下位置下载 POM
和 JAR
文件:
maven.google.com/group_path/library/version/library-version.ext
例如:maven.google.com/android/arch/lifecycle/compiler/1.0.0/compiler-1.0.0.pom
。
SDK 管理器中的离线代码库
对于无法从 Google Maven 代码库中获得的库(通常是旧版库),您必须从 SDK 管理器
下载离线 Google 代码库软件包。
然后,您可以照常将这些库添加到 dependencies
代码块中。
离线库保存在 android_sdk/extras/
中。
依赖项的列出顺序指明了每个库的优先级:第一个库的优先级高于第二个,第二个库的优先级高于第三个,依此类推
。在合并资源
或将清单元素从库中合并到应用中
时,此顺序很重要。
例如,如果您的项目声明以下内容:
依赖 LIB_A 和 LIB_B(按此顺序)
LIB_A 依赖于 LIB_C 和 LIB_D(按此顺序)
LIB_B 也依赖于 LIB_C
那么,扁平型依赖项顺序将如下所示:
LIB_A
LIB_D
LIB_B
LIB_C
这可以确保 LIB_A 和 LIB_B 都可以替换 LIB_C;并且 LIB_D 的优先级仍高于 LIB_B,因为 LIB_A(依赖前者)的优先级高于 LIB_B。
如需详细了解如何合并来自不同项目来源/依赖项的清单,请参阅合并多个清单文件
。
一些直接依赖项可能具有自己的依赖项。此类依赖项称为“传递依赖项”。Gradle 将会自动为您收集并添加这些传递依赖项,无需您手动逐一加以声明。Android Plugin for Gradle 提供了一项任务,用来列出 Gradle 为给定模块解析的依赖项。
对于每个模块,报告还会根据构建变体、测试源代码集和类路径对依赖项进行分组。下面是一个应用模块的依赖项示例报告,其中按该模块的调试构建变体的运行时类路径和该模块的插桩测试源代码集的编译类路径对依赖项进行了分组。
debugRuntimeClasspath - Dependencies for runtime/packaging
+--- :mylibrary (variant: debug)
+--- com.google.android.material:material:1.0.0@aar
+--- androidx.appcompat:appcompat:1.0.2@aar
+--- androidx.constraintlayout:constraintlayout:1.1.3@aar
+--- androidx.fragment:fragment:1.0.0@aar
+--- androidx.vectordrawable:vectordrawable-animated:1.0.0@aar
+--- androidx.recyclerview:recyclerview:1.0.0@aar
+--- androidx.legacy:legacy-support-core-ui:1.0.0@aar
...
debugAndroidTest
debugAndroidTestCompileClasspath - Dependencies for compilation
+--- androidx.test.ext:junit:1.1.0@aar
+--- androidx.test.espresso:espresso-core:3.1.1@aar
+--- androidx.test:runner:1.1.1@aar
+--- junit:junit:4.12@jar
...
如需运行该任务,请按以下步骤操作:
View > Tool Windows > Gradle(或点击工具窗口栏中的 Gradle 图标 )
。AppName > Tasks > android,然后双击 androidDependencies
。Gradle执行该任务后,系统应该会打开 Run 窗口以显示输出。如需详细了解如何管理 Gradle 中的依赖项,请参阅 Gradle 用户指南中的依赖项管理基础知识。
当您向应用项目添加多个依赖项时,这些直接和传递依赖项可能会相互冲突。Android Gradle 插件会尝试妥善解决这些冲突,但有些冲突可能会导致编译时或运行时错误。
为帮助您调查是哪些依赖项导致了错误,请检查您的应用的依赖项树,从中查找出现了多次或存在版本冲突的依赖项。
如果无法轻松识别重复的依赖项,请尝试使用 Android Studio 的界面搜索包含重复类的依赖项,具体操作步骤如下:
从菜单栏中依次选择 Navigate > Class。
在弹出式搜索对话框中,确保已勾选 Include non-project items 旁边的框。
输入出现在构建错误中的类的名称。
检查结果以查找包含该类的依赖项。
下面几部分介绍您可能会遇到的不同类型的依赖项解析错误及其修复方法。
如果某个类多次出现在运行时类路径上,您会收到一条与以下内容类似的错误:
Program type already present com.example.MyClass
此错误通常是下列其中一种情况所致:
二进制文件依赖项包含一个库,该库也作为直接依赖项包含在您的应用中。例如,您的应用声明直接依赖于库 A 和库 B,但库 A已在其二进制文件中包含库 B。
如需解决此问题,请取消将库 B 作为直接依赖项。
您的应用的本地二进制文件依赖项和远程二进制文件依赖项是同一个库
如需解决此问题,请移除其中一个二进制文件依赖项。
解决类路径之间的冲突
当 Gradle 解析编译类路径时,会先解析运行时类路径,然后使用所得结果确定应添加到编译类路径的依赖项版本。换句话说,运行时类路径决定了下游类路径上完全相同的依赖项所需的版本号
。
应用的运行时类路径还决定了 Gradle 需要对应用的测试 APK 的运行时类路径中的匹配依赖项使用的版本号。图 1 说明了类路径的层次结构。
例如,当应用使用 implementation 依赖项配置
添加某个依赖项的一个版本,而库模块使用 runtimeOnly
配置添加该依赖项的另一个版本时,就可能发生多个类路径中出现同一依赖项的不同版本的冲突。
在解析对运行时和编译时类路径的依赖关系时,Android Gradle 插件 3.3.0 及更高版本会尝试自动解决某些下游版本冲突。例如,如果运行时类路径包含库 A 版本 2.0,而编译类路径包含库 A 版本 1.0,则插件会自动将对编译类路径的依赖关系更新为库 A 版本 2.0,以避免错误。
不过,如果运行时类路径包含库 A 版本 1.0,而编译类路径包含库 A 版本 2.0,插件不会将对编译类路径的依赖关系降级为库 A 版本 1.0,您仍会收到一条与以下内容类似的错误:
Conflict with dependency 'com.example.library:some-lib:2.0' in project 'my-library'.
Resolved versions for runtime classpath (1.0) and compile classpath (2.0) differ.
如需解决此问题,请执行以下某项操作:
配置项目全局属性
,以确保每个依赖项的版本在整个项目中保持一致。本部分介绍的高级主题在您要扩展 Android Gradle 插件或编写自己的插件时很有用。
向自定义逻辑发布变体依赖项
库可以包含其他项目或子项目可能要使用的功能。发布库是向其消费者提供库的过程。库可以控制其消费者在编译时和运行时可访问的依赖项。
有两种不同的配置,它们包含每个类路径的传递依赖项,消费者为了使用相应库而必须使用这些依赖项,具体说明如下:
variant_nameApiElements
:此配置包含编译时消费者可使用的传递依赖项。variant_nameRuntimeElements
:此配置包含消费者在运行时可使用的传递依赖项。如需详细了解不同配置之间的关系,请参阅 Java 库插件配置。
自定义依赖项解析策略
一个项目可能会依赖于同一个库的两个不同版本,这样会导致依赖项冲突。例如,如果您的项目依赖于模块 A 的版本 1 和模块 B 的版本 2,而模块 A 以传递方式依赖于模块 B 的版本 3,则会出现依赖项版本冲突。
为了解决此冲突,Android Gradle 插件使用以下依赖项解析策略:当插件检测到依赖项关系图中存在同一模块的不同版本时,默认情况下,它会选择版本号最高的一个。
不过,此策略可能并不总是如您所愿。如需自定义依赖项解析策略,请使用以下配置解析任务所需的特定变体依赖项:
variant_nameCompileClasspath
:此配置包含适用于给定变体编译类路径的解析策略。variant_nameRuntimeClasspath
:此配置包含适用于给定变体运行时类路径的解析策略。Android Gradle 插件包含可用于访问每个变体的配置对象的 getter。因此,您可以使用变体 API 查询依赖项解析,如以下示例所示:
android {
applicationVariants.all { variant ->
// Return compile configuration objects of a variant.
variant.getCompileConfiguration().resolutionStrategy {
// Use Gradle's ResolutionStrategy API
// to customize how this variant resolves dependencies.
...
}
// Return runtime configuration objects of a variant.
variant.getRuntimeConfiguration().resolutionStrategy {
...
}
// Return annotation processor configuration of a variant.
variant.getAnnotationProcessorConfiguration().resolutionStrategy {
...
}
}
}
参考文章
Android Studio官网使用Gradle常用配置