Gradle 提示与诀窍

    如果您不熟悉 Gradle,请阅读来学习基础知识。您也可以查阅 Android 插件的 DSL 参考文档来详细了解本页面使用的属性。

    下面是一些可用于管理您的项目的模块及其源代码的配置。要详细了解如何创建和管理项目与模块,请阅读。

    您可以使用模块级 文件中的
    sourceSets
    代码块更改 Gradle 希望为的每个组件收集文件的位置。

    配置项目范围的属性

    对于包含多个模块的项目,在项目级别定义属性,然后在所有模块间共享这些属性可能会非常有用。为此,您可以将额外属性添加到 文件的 ext 代码块中。

    1. buildscript {...}
    2. allprojects {...}
    3.  
    4. // This block encapsulates custom properties and makes them available to all
    5. // modules in the project.
    6. ext {
    7. // The following are only a few examples of the types of properties you can define.
    8. compileSdkVersion = 28
    9. buildToolsVersion = "28.0.3"
    10.  
    11. // You can also use this to specify versions for dependencies. Having consistent
    12. // versions between modules can avoid behavior conflicts.
    13. supportLibVersion = "28.0.0"
    14. ...
    15. }
    16. ...

    要从相同项目中的模块访问这些属性,请在模块级 build.gradle 文件中使用以下语法。

    1. android {
    2. // Use the following syntax to access properties you defined at the project level:
    3. // rootProject.ext.property_name
    4. compileSdkVersion rootProject.ext.compileSdkVersion
    5. buildToolsVersion rootProject.ext.buildToolsVersion
    6. ...
    7. }
    8. ...
    9. dependencies {
    10. compile "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
    11. ...
    12. }

    管理库和依赖项

    Gradle 提供了一种稳健的机制来,不管它们是远程库还是本地库模块

    将依赖项配置针对特定构建

    如果您希望某个依赖项仅用于特定的源集或者测试源集,则必须大写名称并在其前面加上构建变体或测试源集的名称作为前缀。

    1. android {...}
    2.  
    3. // Creates Gradle dependency configurations to use in the dependencies block.
    4. configurations {
    5. // For variants that combine a product flavor and build type, you need to
    6. // intitialize a placeholder for its dependency configuration.
    7. freeDebugApk {}
    8. ...
    9. }
    10.  
    11. dependencies {
    12. // Adds a compile dependency only to the "free" product flavor.
    13. freeCompile 'com.google.firebase:firebase-ads:9.8.0'
    14. // Adds an apk dependency only to the "freeDebug" build variant.
    15. freeDebugApk fileTree(dir: 'libs', include: ['*.jar'])
    16. // Adds a remote binary dependency only for local tests.
    17. testCompile 'junit:junit:4.12'
    18. // Adds a remote binary dependency only for the instrumented test APK.
    19. androidTestCompile 'com.android.support.test.espresso:espresso-core:3.0.2'
    20. }

    发布库的非默认变体

    您可以将以下代码添加到库的 build.gradle 文件中,更改 Gradle 发布到其他模块的默认库变体:

    1. android {
    2. ...
    3. // If the library configures product flavors, you must specify
    4. // a build variant by its full configuration name. The following
    5. // sets the "demoDebug" build variant as the default version
    6. // of the library that Gradle should publish.
    7. defaultPublishConfig "demoDebug"
    8. }

    您也可以指示 Gradle 发布库的所有可用变体。

    1. android {
    2. ...
    3. // Note that this might increase build times because Gradle must
    4. // build multiple AARs, instead of only one.
    5. publishNonDefault true
    6. }

    通过设置 publishNonDefault true,您可以配置应用模块的 build.gradle 文件,使它的每一个变体都仅使用需要的库版本。

    1. android {...}
    2. ...
    3.  
    4. // Creates Gradle dependency configurations to use in the dependencies block.
    5. configurations {
    6. // Initializes placeholders for the demoDebugCompile and fullReleaseCompile
    7. // dependency configurations.
    8. demoDebugCompile {}
    9. fullReleaseCompile {}
    10. ...
    11. }
    12.  
    13. dependencies {
    14. // If the library configures multiple build variants using product flavors,
    15. // you must target one of the library's variants using its full configuration name.
    16. demoDebugCompile project(path: ':my-library-module', configuration: 'demoDebug')
    17. fullReleaseCompile project(path: ':my-library-module', configuration: 'fullRelease')
    18. ...
    19. }

    Gradle 和 Android 插件允许您通过配置构建变体的方式从一个模块创建不同版本的应用。

    配置多 APK 支持

    利用 Android 插件,您可以,让每一个都针对不同的 ABI 或屏幕密度,并充分利用 Google Play 的多 APK 支持

    按屏幕密度配置单独的 APK

    1. android {
    2. ...
    3. splits {
    4.  
    5. // Configures multiple APKs based on screen density.
    6. density {
    7.  
    8. // Enables building multiple APKs.
    9. enable true
    10.  
    11. // Specifies a list of screen densities Gradle should not create APKs for.
    12. exclude "ldpi", "mdpi"
    13.  
    14. // Alternatively, you can use the following to clear the default list of
    15. // screen densities and specify only the screen densities you want to build
    16. // APKs for:
    17. // reset()
    18. // include "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"
    19.  
    20. // Specifies a list of compatible screen size settings. This property
    21. // configures the element in the manifest. You
    22. // typically don't need to configure this manifest property, but it's
    23. // important when building multiple APKs based on screen density.
    24. compatibleScreens 'normal', 'large', 'xlarge'
    25. }
    26. }
    27. }

    按 ABI 配置单独的 APK

    要为每个 ABI 创建单独的 APK,请将 android.splits.abi 代码块添加到您的模块的 build.gradle 文件中。

    1. android {
    2. ...
    3. splits {
    4.  
    5. // Configures multiple APKs based on ABI.
    6. abi {
    7.  
    8. // Enables building multiple APKs.
    9. enable true
    10.  
    11. // By default all ABIs are included, so use reset() and include to specify that we only
    12. // want APKs for x86, armeabi-v7a, and mips.
    13. reset()
    14.  
    15. // Specifies a list of ABIs that Gradle should create APKs for.
    16. include "x86", "armeabi-v7a", "mips"
    17.  
    18. // Specify that we want to also generate a universal APK that includes all ABIs.
    19. universalApk true
    20. }
    21. }
    22. }

    配置动态版本代码

    默认情况下,在 Gradle 为您的项目生成 APK 时,每个 APK 都有相同的版本信息,此信息在模块级 build.gradle 文件中指定。由于 Google Play 商店不允许同一个应用的多个 APK 全都具有相同的版本信息,在上传到 Play 商店之前,您需要确保每个 APK 都有自己唯一的 。

    为此,您可以使用自定义构建逻辑在构建时向每个 APK 分配不同的版本代码。例如,在为每个 ABI 创建单独的 APK 时,自动 APK 版本控制将如下所示:

    1. ...
    2. defaultConfig {
    3. ...
    4. versionCode 4
    5. }
    6. splits {
    7. ...
    8. }
    9. }
    10.  
    11. // Map for the version code that gives each ABI a value.
    12. ext.abiCodes = ['armeabi-v7a':1, mips:2, x86:3]
    13.  
    14. // For per-density APKs, create a similar map like this:
    15. // ext.densityCodes = ['hdpi': 1, 'xhdpi': 2, 'xxhdpi': 3, 'xxxhdpi': 4]
    16.  
    17. import com.android.build.OutputFile
    18.  
    19. // For each APK output variant, override versionCode with a combination of
    20. // ext.abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
    21. // is equal to defaultConfig.versionCode. If you configure product flavors that
    22. // define their own versionCode, variant.versionCode uses that value instead.
    23. android.applicationVariants.all { variant ->
    24.  
    25. // Assigns a different version code for each output APK
    26. // other than the universal APK.
    27. variant.outputs.each { output ->
    28.  
    29. // Stores the value of ext.abiCodes that is associated with the ABI for this variant.
    30. def baseAbiVersionCode =
    31. // Determines the ABI for this variant and returns the mapped value.
    32. project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))
    33.  
    34. // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
    35. // the following code does not override the version code for universal APKs.
    36. // However, because we want universal APKs to have the lowest version code,
    37. // this outcome is desirable.
    38. if (baseAbiVersionCode != null) {
    39.  
    40. // Assigns the new version code to versionCodeOverride, which changes the version code
    41. // for only the output APK, not for the variant itself. Skipping this step simply
    42. // causes Gradle to use the value of variant.versionCode for the APK.
    43. output.versionCodeOverride =
    44. baseAbiVersionCode * 1000 + variant.versionCode
    45. }
    46. }
    47. }

    某些情况下,您可能希望组合多个产品风味中的配置。为此,您可以通过 Android Plugin for Gradle 创建产品风味组,称为风味维度

    下面的代码示例使用 ) 属性创建一个“模式”风味维度以组织“完整”和“演示”产品风味,以及一个“api”风味维度以基于 API 级别组织产品风味配置。随后,Gradle 会将“模式”维度的产品风味与“api”维度的产品风味组合。

    过滤变体

    您可以使用模块的 build.gradle 文件中的 variantFilter 代码块,将您不想要的变体过滤掉。以下示例代码将指示 Gradle 不构建任何可以将“minApi21”与“演示”产品风味组合的变体:

    1. android {
    2. ...
    3. buildTypes {...}
    4.  
    5. flavorDimensions "api", "mode"
    6. productFlavors {
    7. demo {...}
    8. full {...}
    9. minApi24 {...}
    10. minApi23 {...}
    11. minApi21 {...}
    12. }
    13.  
    14. variantFilter { variant ->
    15. def names = variant.flavors*.name
    16. // To check for a build type instead, use variant.buildType.name == "buildType"
    17. if (names.contains("minApi21") && names.contains("demo")) {
    18. // Gradle ignores any variants that satisfy the conditions above.
    19. setIgnore(true)
    20. }
    21. }
    22. }
    23. ...

    测试应用

    要详细了解如何运行本地和集成单元测试,请阅读测试应用

    配置 lint 选项

    您可以使用模块级 文件中的 代码块配置特定的 lint 选项。要详细了解如何为您的 Android 项目使用 lint,请阅读使用 Lint 改进您的代码

    1. android {
    2. ...
    3. lintOptions {
    4. // Turns off checks for the issue IDs you specify.
    5. disable 'TypographyFractions','TypographyQuotes'
    6. // Turns on checks for the issue IDs you specify. These checks are in
    7. // addition to the default lint checks.
    8. enable 'RtlHardcoded', 'RtlCompat', 'RtlEnabled'
    9. // To enable checks for only a subset of issue IDs and ignore all others,
    10. // list the issue IDs with the 'check' property instead. This property overrides
    11. // any issue IDs you enable or disable using the properties above.
    12. check 'NewApi', 'InlinedApi'
    13. // If set to true, turns off analysis progress reporting by lint.
    14. quiet true
    15. // if set to true (default), stops the build if errors are found.
    16. abortOnError false
    17. // if true, only report errors.
    18. ignoreWarnings true
    19. }
    20. }
    21. ...

    配置仪器 manifest 设置

    在 Gradle 构建您的测试 APK 时,它会自动生成 文件并为其配置 <instrumentation> 节点。您可以在中创建另一个 manifest 文件或者配置您的模块级 build.gradle 文件,通过这两种方式更改此节点的一些设置,如以下代码示例中所示。

    1. android {
    2. ...
    3. // Each product flavor you configure can override properties in the
    4. // defaultConfig block. To learn more, go to Configure Product Flavors.
    5. defaultConfig {
    6. ...
    7. // Specifies the for the test APK.
    8. testApplicationId "com.test.foo"
    9. // Specifies the fully-qualified class name of the test instrumentation runner.
    10. "android.test.InstrumentationTestRunner"
    11. // If set to 'true', enables the instrumentation class to start and stop profiling.
    12. // If set to false (default), profiling occurs the entire time the instrumentation
    13. // class is running.
    14. testHandleProfiling true
    15. // If set to 'true', indicates that the Android system should run the instrumentation
    16. // class as a functional test. The default value is 'false'
    17. true
    18. }
    19. }
    20. ...

    更改测试构建类型

    默认情况下,所有测试均针对调试构建类型运行。您可以利用模块级 build.gradle 文件中的 testBuildType 属性将其更改为其他构建类型。例如,如果您想针对“staging”构建类型运行测试,请按下面这段代码中所示对该文件进行编辑。

    1. android {
    2. ...
    3. testBuildType "staging"
    4. }

    配置 Gradle 测试选项

    要指定可以更改 Gradle 运行所有测试方式的选项,请配置模块级 build.gradle 中的 testOptions 代码块。

    1. android {
    2. ...
    3. // Encapsulates options for running tests.
    4. testOptions {
    5. // Changes the directory where Gradle saves test reports. By default, Gradle saves test reports
    6. // in the path_to_your_project/module_name/build/outputs/reports/ directory.
    7. // '$rootDir' sets the path relative to the root directory of the current project.
    8. reportDir "$rootDir/test-reports"
    9. // Changes the directory where Gradle saves test results. By default, Gradle saves test results
    10. // in the path_to_your_project/module_name/build/outputs/test-results/ directory.
    11. // '$rootDir' sets the path relative to the root directory of the current project.
    12. resultsDir "$rootDir/test-results"
    13. }
    14. }

    要仅为本地单元测试指定选项,请配置 代码块。

    1. android {
    2. ...
    3. testOptions {
    4. ...
    5. // Encapsulates options for local unit tests.
    6. unitTests {
    7. // By default, local unit tests throw an exception any time the code you are testing tries to access
    8. // Android platform APIs (unless you mock Android dependencies yourself or with a testing
    9. // framework like Mockito). However, you can enable the following property so that the test
    10. // returns either null or zero when accessing platform APIs, rather than throwing an exception.
    11. returnDefaultValues true
    12.  
    13. // Encapsulates options for controlling how Gradle executes local unit tests. For a list
    14. // of all the options you can specify, read .
    15. all {
    16. // Sets JVM argument(s) for the test JVM(s).
    17. jvmArgs '-XX:MaxPermSize=256m'
    18.  
    19. // You can also check the task name to apply options to only the tests you specify.
    20. if (it.name == 'testDebugUnitTest') {
    21. systemProperty 'debug', 'true'
    22. }
    23. }
    24. }
    25. }
    26. }

    Android Studio 使用 ProGuard 来压缩代码。对于新项目,Android Studio 将使用 Android SDK tools/proguard/folder 下的默认设置文件 (proguard-android.txt)。要想进一步压缩代码,请尝试使用位于同一位置的 proguard-android-optimize.txt 文件。

    1. android {
    2. buildTypes {
    3. release {
    4. proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
    5. 'proguard-rules.pro'
    6. }
    7. }
    8. ...
    9. }
    10. ...

    要添加特定于每个构建变体的 ProGuard 规则,请为每个风味配置其他 属性。例如,以下示例会将 flavor2-rules.pro 添加到“flavor2”中。现在,发布版本“flavor2”使用所有三个 ProGuard 规则,因为还应用了来自 release 代码块的规则。

    1. android {
    2. ...
    3. buildTypes {
    4. release {
    5. minifyEnabled true
    6. proguardFiles getDefaultProguardFile('proguard-android.txt'),
    7. 'proguard-rules.pro'
    8. }
    9. }
    10. productFlavors {
    11. flavor1 {
    12. ...
    13. }
    14. flavor2 {
    15. proguardFile 'flavor2-rules.pro'
    16. }
    17. }
    18. }
    19. ...

    通过 Instant Run 启用代码压缩

    通过 Instant Run 启用代码压缩,只需将 useProguard 设为 (并保持 minifyEnabled 设为 true)。这将使用实验性代码压缩器,它不会对您的代码进行混淆处理或优化(因此,您应当仅为“调试”构建类型启用此压缩器)。

    1. android {
    2. buildTypes {
    3. debug {
    4. minifyEnabled true
    5. useProguard false
    6. proguardFiles getDefaultProguardFile('proguard-android.txt'),
    7. 'proguard-rules.pro'
    8. }
    9. release {
    10. minifyEnabled true
    11. proguardFiles getDefaultProguardFile('proguard-android.txt'),
    12. 'proguard-rules.pro'
    13. }
    14. }
    15. }

    配置 DEX 选项

    在 Gradle 将您的代码编译到 DEX 文件中时,使用以下属性可以缩短构建时间。

    发布您的应用

    要详细了解如何将使用的应用发布到 Google Play,请阅读。

    签署您的应用

    尽管 Android Studio 提供了一种从界面为发布构建配置签署的简单方式,您仍然可以手动配置模块的 build.gradle 文件中的 代码块:

    1. android {
    2. ...
    3. defaultConfig { ... }
    4.  
    5. // Encapsulates signing configurations.
    6. signingConfigs {
    7. // Creates a signing configuration called "release".
    8. release {
    9. // Specifies the path to your keystore file.
    10. storeFile file("my-release-key.jks")
    11. // Specifies the password for your keystore.
    12. storePassword "password"
    13. // Specifies the identifying name for your key.
    14. keyAlias "my-alias"
    15. // Specifies the password for your key.
    16. keyPassword "password"
    17. }
    18. }
    19. buildTypes {
    20. release {
    21. // Adds the "release" signing configuration to the release build type.
    22. signingConfig signingConfigs.release
    23. ...
    24. }
    25. }
    26. }
    27. ...

    从您的项目中移除私密签署信息

    默认情况下,签署配置将以纯文本形式记录到模块的 build.gradle 文件中。如果您正在与某个团队合作或者参与一个开放源代码项目,可以执行以下步骤,将此敏感信息移出构建文件。

    • 在项目的根目录下创建一个名为 keystore.properties 的文件,并包含以下信息:
    1. storePassword=myStorePassword
    2. keyPassword=myKeyPassword
    3. keyAlias=myKeyAlias
    4. storeFile=myStoreFileLocation
    • 在您的 build.gradle 文件中,按以下步骤操作来加载 keystore.properties 文件(必须在 android 代码块之前):
    1. // Creates a variable called keystorePropertiesFile, and initializes it to the
    2. // keystore.properties file.
    3. def keystorePropertiesFile = rootProject.file("keystore.properties")
    4.  
    5. // Initializes a new Properties() object called keystoreProperties.
    6. def keystoreProperties = new Properties()
    7.  
    8. // Loads the keystore.properties file into the keystoreProperties object.
    9. keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
    10.  
    11. android {
    12. ...
    13. }
    14. ...
    • 输入存储在 keystoreProperties 对象中的签署信息:
    1. android {
    2. signingConfigs {
    3. config {
    4. keyAlias keystoreProperties['keyAlias']
    5. keyPassword keystoreProperties['keyPassword']
    6. storeFile file(keystoreProperties['storeFile'])
    7. storePassword keystoreProperties['storePassword']
    8. }
    9. }
    10. ...
    11. }
    12. ...
    • 点击通知栏中的 Sync Now
      要详细了解应用签署,请阅读签署您的应用

    下面的提示有助于简化您的 Android 应用开发。

    与应用的代码共享自定义字段和资源值

    在构建时,Gradle 将生成 BuildConfig 类,以便您的应用代码可以检查与当前构建有关的信息。您也可以使用 buildConfigField() 函数,将自定义字段添加到 Gradle 构建配置文件的 BuildConfig 类中,然后在应用的运行时代码中访问这些值。同样,您也可以使用 resValue() 添加应用资源值。

    1. android {
    2. ...
    3. buildTypes {
    4. release {
    5. // These values are defined only for the release build, which
    6. // is typically used for full builds and continuous builds.
    7. buildConfigField("String", "BUILD_TIME", "\"${minutesSinceEpoch}\"")
    8. resValue("string", "build_time", "${minutesSinceEpoch}")
    9. ...
    10. }
    11. debug {
    12. // Use static values for incremental builds to ensure that
    13. // resource files and BuildConfig aren't rebuilt with each run.
    14. // If they were dynamic, they would prevent certain benefits of
    15. // Instant Run as well as Gradle UP-TO-DATE checks.
    16. buildConfigField("String", "BUILD_TIME", "\"0\"")
    17. resValue("string", "build_time", "0")
    18. }
    19. }
    20. }
    21. ...

    在您的应用代码中,您可以访问以下属性:

    1. ...
    2. Log.i(TAG, BuildConfig.BUILD_TIME);
    3. Log.i(TAG, getString(R.string.build_time));

    某些情况下,您可能需要同时在 manifest 和代码中声明相同属性(例如,在为 声明机构时)。如以下示例中所示,请在模块的 build.gradle 文件中定义一个属性并使其对 manifest 和代码均可用,而不必在多个位置更新相同的属性以反映更改。要了解详情,请阅读将构建变量注入 Manifest

    1. android {
    2. // For settings specific to a product flavor, configure these properties
    3. // for each flavor in the productFlavors block.
    4. defaultConfig {
    5. // Creates a property for the authority.
    6. def filesAuthorityValue = applicationId + ".files"
    7. // Creates a placeholder property to use in the manifest.
    8. manifestPlaceholders =
    9. [filesAuthority: filesAuthorityValue]
    10. // Adds a new field for the authority to the BuildConfig class.
    11. buildConfigField("String",
    12. "FILES_AUTHORITY",
    13. "\"${filesAuthorityValue}\"")
    14. }
    15. ...
    16. }
    17. ...

    在您的 manifest 中,访问以下占位符:

    1. <manifest>
    2. ...
    3. <application>
    4. ...
    5. <provider
    6. android:name="android.support.v4.content.FileProvider"
    7. android:authorities="${filesAuthority}"
    8. android:exported="false"
    9. android:grantUriPermissions="true">
    10. ...
    11. </provider>
    12. </application>
    13. </manifest>
    1. ...
    2. Uri contentUri = FileProvider.getUriForFile(getContext(),
    3. myFile);