Gradle 技巧和食谱

Gradle 和 Android Gradle 插件提供了一种灵活的方式来编译、构建和打包您的 Android 应用程序或库。此页面收集了一些有用的技巧和配置,以帮助您充分利用每次构建。如果您想了解如何使构建速度更快,请阅读 优化构建速度

如果您不熟悉 Gradle,请阅读 配置构建 来学习基础知识。您还可以检查 Android 插件的 DSL 参考文档,以了解有关此页面中使用的属性的更多信息。

管理项目和源代码

以下是一些用于管理项目模块及其源代码的配置。要了解有关创建和管理项目和模块的更多信息,请阅读 项目概述

更改默认源集配置

您可以在模块级 build.gradle 文件中使用 sourceSets 块来更改 Gradle 在何处查找以收集每个 源集 组件的文件。

Groovy

android {
  ...
  sourceSets {
    // Encapsulates configurations for the main source set.
    main {
      // Changes the directory for Java sources. The default directory is
      // 'src/main/java'.
      java.srcDirs = ['other/java']

      // When you list multiple directories, Gradle uses all of them to collect
      // sources. You should avoid specifying a directory which is a parent to one
      // or more other directories you specify.
      res.srcDirs = ['other/res1', 'other/res2']

      // For each source set, you can specify only one Android manifest.
      // The following points Gradle to a different manifest for this source set.
      manifest.srcFile 'other/AndroidManifest.xml'
      ...
    }

    // Create additional blocks to configure other source sets.
    androidTest {

      // If all the files for a source set are located under a single root
      // directory, you can specify that directory using the setRoot property.
      // When gathering sources for the source set, Gradle looks only in locations
      // relative to the root directory you specify. For example, after applying
      // the configuration below for the androidTest source set, Gradle looks for
      // Java sources only in the src/tests/java/ directory.
      setRoot 'src/tests'
      ...
    }
  }
}
...

Kotlin

android {
  ...
  sourceSets {
    // Encapsulates configurations for the main source set.
    getByName("main") {
      // Changes the directory for Java sources. The default directory is
      // 'src/main/java'.
      java.setSrcDirs("other/java")

      // When you list multiple directories, Gradle uses all of them to collect
      // sources. You should avoid specifying a directory which is a parent to one
      // or more other directories you specify.
      res.setSrcDirs("other/res1", "other/res2")

      // For each source set, you can specify only one Android manifest.
      // The following points Gradle to a different manifest for this source set.
      manifest.srcFile("other/AndroidManifest.xml")
      ...
    }

    // Create additional blocks to configure other source sets.
    androidTest {

      // If all the files for a source set are located under a single root
      // directory, you can specify that directory using the setRoot property.
      // When gathering sources for the source set, Gradle looks only in locations
      // relative to the root directory you specify. For example, after applying
      // the configuration below for the androidTest source set, Gradle looks for
      // Java sources only in the src/tests/java/ directory.
      setRoot("src/tests")
      ...
    }
  }
}
...

管理库和依赖项

Gradle 提供了一种强大的机制来 管理依赖项,无论它们是远程库还是本地 库模块

使用依赖项配置来定位特定构建

如果您只希望特定 构建变体 源集或 测试源集 具有依赖项,请大写 依赖项配置 名称并在其前面加上构建变体或测试源集的名称。

Groovy

android {...}

// Creates Gradle dependency configurations to use in the dependencies block.
configurations {
  // For variants that combine a product flavor and build type, you need to
  // intitialize a placeholder for its dependency configuration.
  freeDebugRuntimeOnly{}
  ...
}

dependencies {
    // Adds an implementation dependency only to the "free" product flavor.
    freeImplementation 'com.google.firebase:firebase-ads:21.5.1'
    // Adds a runtimeOnly dependency only to the "freeDebug" build variant.
    freeDebugRuntimeOnly fileTree(dir: 'libs', include: ['*.jar'])
    // 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.6.1'
}

Kotlin

android {...}

dependencies {
    // Use ""() notation for custom flavors and build types
    // Adds an implementation dependency only to the "free" product flavor.
    "freeImplementation"("com.google.firebase:firebase-ads:21.5.1")
    // Adds a runtimeOnly dependency only to the "freeDebug" build variant.
    "freeDebugRuntimeOnly"(fileTree("dir" to "libs", "include" to "*.jar"))
    // 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.6.1")
}

创建应用程序的不同版本

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

配置动态版本代码

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

您可以使用在构建时为每个 APK 分配不同版本代码的自定义构建逻辑来实现这一点。例如,在为每个 ABI 创建单独的 APK 时,自动 APK 版本控制看起来类似于以下内容

Groovy

android {
  ...
  defaultConfig {
    ...
    versionCode 4
  }
  splits {
    ...
  }
}

// Map for the version code that gives each ABI a value.
ext.abiCodes = ['armeabi-v7a':1, mips:2, x86:3]

// For per-density APKs, create a similar map like this:
// ext.densityCodes = ['hdpi': 1, 'xhdpi': 2, 'xxhdpi': 3, 'xxxhdpi': 4]

import com.android.build.OutputFile

// For each APK output variant, override versionCode with a combination of
// ext.abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
android.applicationVariants.all { variant ->

  // Assigns a different version code for each output APK
  // other than the universal APK.
  variant.outputs.each { output ->

    // Stores the value of ext.abiCodes that is associated with the ABI for this variant.
    def baseAbiVersionCode =
            // Determines the ABI for this variant and returns the mapped value.
            project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))

    // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
    // the following code does not override the version code for universal APKs.
    // However, because we want universal APKs to have the lowest version code,
    // this outcome is desirable.
    if (baseAbiVersionCode != null) {

      // Assigns the new version code to versionCodeOverride, which changes the version code
      // for only the output APK, not for the variant itself. Skipping this step simply
      // causes Gradle to use the value of variant.versionCode for the APK.
      output.versionCodeOverride =
              baseAbiVersionCode * 1000 + variant.versionCode
    }
  }
}

Kotlin

android {
  ...
  defaultConfig {
    ...
    versionCode = 4
  }
  splits {
    ...
  }
}

// Map for the version code that gives each ABI a value.
val abiCodes = mapOf("armeabi-v7a" to 1, "mips" to 2, "x86" to 3)

// For per-density APKs, create a similar map like this:
// val densityCodes = mapOf("hdpi" to 1, "xhdpi" to 2, "xxhdpi" to 3, "xxxhdpi" to 4)

import com.android.build.api.variant.FilterConfiguration.FilterType.*

// For each APK output variant, override versionCode with a combination of
// abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
androidComponents {
    onVariants { variant ->

        // Assigns a different version code for each output APK
        // other than the universal APK.
        variant.outputs.forEach { output ->
            val name = output.filters.find { it.filterType == ABI }?.identifier

            // Stores the value of abiCodes that is associated with the ABI for this variant.
            val baseAbiCode = abiCodes[name]
            // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
            // the following code does not override the version code for universal APKs.
            // However, because we want universal APKs to have the lowest version code,
            // this outcome is desirable.
            if (baseAbiCode != null) {
                // Assigns the new version code to output.versionCode, which changes the version code
                // for only the output APK, not for the variant itself.
                output.versionCode.set(baseAbiCode * 1000 + (output.versionCode.get() ?: 0))
            }
        }
    }
}

组合多个产品风味

在某些情况下,您可能希望 组合来自多个产品风味的配置。为此,Android Gradle 插件允许您创建产品风味组,称为“风味维度”。

以下代码示例使用 flavorDimensions 属性创建一个“mode”风味维度来对“full”和“demo”产品风味进行分组,以及一个“api”风味维度来对基于 API 级别进行分组的产品风味配置。然后,Gradle 会将“mode”维度中的产品风味与“api”维度中的产品风味组合起来。

Groovy

android {
  ...
  buildTypes {
    debug {...}
    release {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list each dimension determines its priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.
  flavorDimensions "api", "mode"

  productFlavors {
    demo {
      // Assigns this product flavor to the "mode" flavor dimension.
      dimension "mode"
      ...
    }

    full {
      dimension "mode"
      ...
    }

    // Configurations in the "api" product flavors override those in "mode"
    // flavors and the defaultConfig block. Gradle determines the priority
    // between flavor dimensions based on the order in which they appear next
    // to the flavorDimensions property above--the first dimension has a higher
    // priority than the second, and so on.
    minApi24 {
      dimension "api"
      minSdkVersion '24'
      // To ensure the target device receives the version of the app with
      // the highest compatible API level, assign version codes in increasing
      // value with API level. To learn more about assigning version codes to
      // support app updates and uploading to Google Play, read Multiple APK Support
      versionCode 30000 + android.defaultConfig.versionCode
      versionNameSuffix "-minApi24"
      ...
    }

    minApi23 {
      dimension "api"
      minSdkVersion '23'
      versionCode 20000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi23"
      ...
    }

    minApi21 {
      dimension "api"
      minSdkVersion '21'
      versionCode 10000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi21"
      ...
    }
  }
}
...

Kotlin

android {
  ...
  buildTypes {
    getByName("debug") {...}
    getByName("release") {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list each dimension determines its priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.
  flavorDimensions += listOf("api", "mode")

  productFlavors {
    create("demo") {
      // Assigns this product flavor to the "mode" flavor dimension.
      dimension = "mode"
      ...
    }

    create("full") {
      dimension = "mode"
      ...
    }

    // Configurations in the "api" product flavors override those in "mode"
    // flavors and the defaultConfig block. Gradle determines the priority
    // between flavor dimensions based on the order in which they appear next
    // to the flavorDimensions property above--the first dimension has a higher
    // priority than the second, and so on.
    create("minApi24") {
      dimension = "api"
      minSdkVersion(24)
      // To ensure the target device receives the version of the app with
      // the highest compatible API level, assign version codes in increasing
      // value with API level. To learn more about assigning version codes to
      // support app updates and uploading to Google Play, read Multiple APK Support
      versionCode = 30000 + android.defaultConfig.versionCode
      versionNameSuffix = "-minApi24"
      ...
    }

    create("minApi23") {
      dimension = "api"
      minSdkVersion(23)
      versionCode = 20000  + android.defaultConfig.versionCode
      versionNameSuffix = "-minApi23"
      ...
    }

    create("minApi21") {
      dimension = "api"
      minSdkVersion(21)
      versionCode = 10000  + android.defaultConfig.versionCode
      versionNameSuffix = "-minApi21"
      ...
    }
  }
}
...

筛选变体

您可以 筛选 您不希望使用的构建变体,方法是在模块的 build.gradle 文件中使用 variantFilter 块。以下示例代码告诉 Gradle 不要构建任何将“minApi21”和“demo”产品风味组合起来的变体

Groovy

android {
  ...
  buildTypes {...}

  flavorDimensions "api", "mode"
  productFlavors {
    demo {...}
    full {...}
    minApi24 {...}
    minApi23 {...}
    minApi21 {...}
  }

  variantFilter { variant ->
      def names = variant.flavors*.name
      // To check for a certain build type, use variant.buildType.name == "<buildType>"
      if (names.contains("minApi21") && names.contains("demo")) {
          // Gradle ignores any variants that satisfy the conditions above.
          setIgnore(true)
      }
  }
}
...

Kotlin

android {
  ...
  buildTypes {...}

  flavorDimensions "api", "mode"
  productFlavors {
    create("demo") {...}
    create("full") {...}
    create("minApi24") {...}
    create("minApi23") {...}
    create("minApi21") {...}
  }
}

androidComponents {
    beforeVariants { variantBuilder ->
        // To check for a certain build type, use variantBuilder.buildType == "<buildType>"
        if (variantBuilder.productFlavors.containsAll(listOf("api" to "minApi21", "mode" to "demo"))) {
            // Gradle ignores any variants that satisfy the conditions above.
            variantBuilder.enabled = false
        }
    }
}
...

测试您的应用程序

要了解有关运行本地和集成单元测试的更多信息,请阅读 测试您的应用程序

配置 lint 选项

您可以使用模块级 build.gradle 文件中的 lintOptions 块配置某些 lint 选项。要了解有关在 Android 项目中使用 lint 的更多信息,请阅读 使用 Lint 改善代码

Groovy

android {
    ...
    lintOptions {
        // Turns off checks for the issue IDs you specify.
        disable 'TypographyFractions','TypographyQuotes'
        // Turns on checks for the issue IDs you specify. These checks are in
        // addition to the default lint checks.
        enable 'RtlHardcoded','RtlCompat', 'RtlEnabled'
        // To enable checks for only a subset of issue IDs and ignore all others,
        // list the issue IDs with the 'check' property instead. This property overrides
        // any issue IDs you enable or disable using the properties above.
        checkOnly 'NewApi', 'InlinedApi'
        // If set to true, turns off analysis progress reporting by lint.
        quiet true
        // if set to true (default), stops the build if errors are found.
        abortOnError false
        // if true, only report errors.
        ignoreWarnings true
    }
}
...

Kotlin

android {
    ...
    lintOptions {
        // Turns off checks for the issue IDs you specify.
        disable("TypographyFractions")
        disable("TypographyQuotes")
        // Turns on checks for the issue IDs you specify. These checks are in
        // addition to the default lint checks.
        enable("RtlHardcoded")
        enable("RtlCompat")
        enable("RtlEnabled")
        // To enable checks for only a subset of issue IDs and ignore all others,
        // list the issue IDs with the 'check' property instead. This property overrides
        // any issue IDs you enable or disable using the properties above.
        checkOnly("NewApi", "InlinedApi")
        // If set to true, turns off analysis progress reporting by lint.
        quiet = true
        // if set to true (default), stops the build if errors are found.
        abortOnError = false
        // if true, only report errors.
        ignoreWarnings = true
    }
}
...

配置测试仪器清单设置

当 Gradle 构建您的测试 APK 时,它会自动生成 AndroidManifest.xml 文件并使用 <instrumentation> 节点对其进行配置。您可以通过在 测试源集 中创建另一个清单文件或配置模块级 build.gradle 文件来更改此节点的某些设置,如以下代码示例所示。

Groovy

android {
  ...
  // Each product flavor you configure can override properties in the
  // defaultConfig block. To learn more, go to Configure Product Flavors.
  defaultConfig {
    ...
    // Specifies the application ID for the test APK.
    testApplicationId "com.test.foo"
    // Specifies the fully-qualified class name of the test instrumentation runner.
    testInstrumentationRunner "android.test.InstrumentationTestRunner"
    // If set to 'true', enables the instrumentation class to start and stop profiling.
    // If set to false (default), profiling occurs the entire time the instrumentation
    // class is running.
    testHandleProfiling true
    // If set to 'true', indicates that the Android system should run the instrumentation
    // class as a functional test. The default value is 'false'
    testFunctionalTest true
  }
}
...

Kotlin

android {
  ...
  // Each product flavor you configure can override properties in the
  // defaultConfig block. To learn more, go to Configure Product Flavors.
  defaultConfig {
    ...
    // Specifies the application ID for the test APK.
    testApplicationId = "com.test.foo"
    // Specifies the fully-qualified class name of the test instrumentation runner.
    testInstrumentationRunner = "android.test.InstrumentationTestRunner"
    // If set to 'true', enables the instrumentation class to start and stop profiling.
    // If set to false (default), profiling occurs the entire time the instrumentation
    // class is running.
    testHandleProfiling = true
    // If set to 'true', indicates that the Android system should run the instrumentation
    // class as a functional test. The default value is 'false'
    testFunctionalTest = true
  }
}
...

更改测试构建类型

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

Groovy

android {
    ...
    testBuildType "staging"
}

Kotlin

android {
    ...
    testBuildType "staging"
}

配置 Gradle 测试选项

要指定更改 Gradle 如何运行所有测试的选项,请在模块级 build.gradle 中配置 testOptions 块。

Groovy

android {
  ...
  // Encapsulates options for running tests.
  testOptions {
    // Changes the directory where Gradle saves test reports. By default, Gradle saves test reports
    // in the path_to_your_project/module_name/build/outputs/reports/ directory.
    // '$rootDir' sets the path relative to the root directory of the current project.
    reportDir "$rootDir/test-reports"
    // Changes the directory where Gradle saves test results. By default, Gradle saves test results
    // in the path_to_your_project/module_name/build/outputs/test-results/ directory.
    // '$rootDir' sets the path relative to the root directory of the current project.
    resultsDir "$rootDir/test-results"
  }
}

Kotlin

android {
  ...
  // Encapsulates options for running tests.
  testOptions {
    // Changes the directory where Gradle saves test reports. By default, Gradle saves test reports
    // in the path_to_your_project/module_name/build/outputs/reports/ directory.
    // '$rootDir' sets the path relative to the root directory of the current project.
    reportDir "$rootDir/test-reports"
    // Changes the directory where Gradle saves test results. By default, Gradle saves test results
    // in the path_to_your_project/module_name/build/outputs/test-results/ directory.
    // '$rootDir' sets the path relative to the root directory of the current project.
    resultsDir "$rootDir/test-results"
  }
}

要仅为本地单元测试指定选项,请配置 testOptions.unitTests 块。

Groovy

android {
  ...
  testOptions {
    ...
    // Encapsulates options for local unit tests.
    unitTests {
      // By default, local unit tests throw an exception any time the code you are testing tries to access
      // Android platform APIs (unless you mock Android dependencies yourself or with a testing
      // framework like Mockito). However, you can enable the following property so that the test
      // returns either null or zero when accessing platform APIs, rather than throwing an exception.
      returnDefaultValues true

      // Encapsulates options for controlling how Gradle executes local unit tests. For a list
      // of all the options you can specify, read Gradle's reference documentation.
      all {
        // Sets JVM argument(s) for the test JVM(s).
        jvmArgs '-XX:MaxPermSize=256m'

        // You can also check the task name to apply options to only the tests you specify.
        if (it.name == 'testDebugUnitTest') {
          systemProperty 'debug', 'true'
        }
      }
    }
  }
}

Kotlin

android {
  ...
  testOptions {
    ...
    // Encapsulates options for local unit tests.
    unitTests {
      // By default, local unit tests throw an exception any time the code you are testing tries to access
      // Android platform APIs (unless you mock Android dependencies yourself or with a testing
      // framework like Mockito). However, you can enable the following property so that the test
      // returns either null or zero when accessing platform APIs, rather than throwing an exception.
      returnDefaultValues true

      // Encapsulates options for controlling how Gradle executes local unit tests. For a list
      // of all the options you can specify, read Gradle's reference documentation.
      all {
        // Sets JVM argument(s) for the test JVM(s).
        jvmArgs '-XX:MaxPermSize=256m'

        // You can also check the task name to apply options to only the tests you specify.
        if (it.name == 'testDebugUnitTest') {
          systemProperty 'debug', 'true'
        }
      }
    }
  }
}

优化您的构建

本节提供了一些配置,以帮助您加快完整和增量构建速度。要了解更多信息,请阅读 优化构建速度

缩减代码

Android Studio 使用 R8(它使用 ProGuard 规则文件)来 缩减代码。对于新项目,Android Studio 使用 Android SDK 的 tools/proguard/folder 中的默认设置文件 (proguard-android.txt)。为了进一步缩减代码,请尝试使用位于同一位置的 proguard-android-optimize.txt 文件。

Groovy

android {
  buildTypes {
    release {
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
                                           'proguard-rules.pro'
    }
  }
  ...
}
...

Kotlin

android {
  buildTypes {
    release {
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
                                           'proguard-rules.pro'
    }
  }
  ...
}
...

要添加特定于每个构建变体的规则,请为每个风味配置额外的 proguardFiles 属性。例如,以下示例将 flavor2-rules.pro 添加到“flavor2”。现在,“flavor2”的发布版本使用所有三个规则文件,因为来自发布块的规则文件也适用。

Groovy

android {
  ...
  buildTypes {
    release {
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-android.txt'),
             'proguard-rules.pro'
    }
  }
  productFlavors {
    flavor1 {
      ...
    }
    flavor2 {
      proguardFile 'flavor2-rules.pro'
    }
  }
}
...

Kotlin

android {
  ...
  buildTypes {
    release {
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-android.txt'),
             'proguard-rules.pro'
    }
  }
  productFlavors {
    flavor1 {
      ...
    }
    flavor2 {
      proguardFile 'flavor2-rules.pro'
    }
  }
}
...

发布您的应用程序

要了解有关将应用程序发布到 Google Play 的更多信息,请阅读 发布您的应用程序

签署您的应用程序

虽然 Android Studio 提供了一种直接的方法来从 UI 配置发布构建的签名,但您也可以手动配置模块的 build.gradle 文件中的 signingConfigs

Groovy

android {
  ...
  defaultConfig { ... }

  // Encapsulates signing configurations.
  signingConfigs {
    // Creates a signing configuration called "release".
    release {
      // Specifies the path to your keystore file.
      storeFile file("my-release-key.jks")
      // Specifies the password for your keystore.
      storePassword "password"
      // Specifies the identifying name for your key.
      keyAlias "my-alias"
      // Specifies the password for your key.
      keyPassword "password"
    }
  }
  buildTypes {
    release {
      // Adds the "release" signing configuration to the release build type.
      signingConfig signingConfigs.release
      ...
    }
  }
}
...

Kotlin

android {
  ...
  defaultConfig { ... }

  // Encapsulates signing configurations.
  signingConfigs {
    // Creates a signing configuration called "release".
    release {
      // Specifies the path to your keystore file.
      storeFile file("my-release-key.jks")
      // Specifies the password for your keystore.
      storePassword "password"
      // Specifies the identifying name for your key.
      keyAlias "my-alias"
      // Specifies the password for your key.
      keyPassword "password"
    }
  }
  buildTypes {
    release {
      // Adds the "release" signing configuration to the release build type.
      signingConfig signingConfigs.release
      ...
    }
  }
}
...

从项目中删除私有签名信息

默认情况下,签名配置以纯文本形式记录在模块的 build.gradle 文件中。如果您与团队或开源项目合作,您可以通过以下步骤将此敏感信息从构建文件移出。

  1. 在项目的根目录中创建一个名为 keystore.properties 的文件,并包含以下信息
    storePassword=myStorePassword
    keyPassword=myKeyPassword
    keyAlias=myKeyAlias
    storeFile=myStoreFileLocation
    
  2. 在您的 build.gradle 文件中,按如下方式加载 keystore.properties 文件(这必须在 android 块之前)

    Groovy

    // Creates a variable called keystorePropertiesFile, and initializes it to the
    // keystore.properties file.
    def keystorePropertiesFile = rootProject.file("keystore.properties")
    
    // Initializes a new Properties() object called keystoreProperties.
    def keystoreProperties = new Properties()
    
    // Loads the keystore.properties file into the keystoreProperties object.
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
    
    android {
      ...
    }
    ...
    

    Kotlin

    // Creates a variable called keystorePropertiesFile, and initializes it to the
    // keystore.properties file.
    def keystorePropertiesFile = rootProject.file("keystore.properties")
    
    // Initializes a new Properties() object called keystoreProperties.
    def keystoreProperties = new Properties()
    
    // Loads the keystore.properties file into the keystoreProperties object.
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
    
    android {
      ...
    }
    ...
    
  3. 输入存储在 keystoreProperties 对象中的签名信息

    Groovy

    android {
      signingConfigs {
        config {
          keyAlias keystoreProperties['keyAlias']
          keyPassword keystoreProperties['keyPassword']
          storeFile file(keystoreProperties['storeFile'])
          storePassword keystoreProperties['storePassword']
        }
      }
      ...
    }
    ...
    

    Kotlin

    android {
      signingConfigs {
        config {
          keyAlias keystoreProperties['keyAlias']
          keyPassword keystoreProperties['keyPassword']
          storeFile file(keystoreProperties['storeFile'])
          storePassword keystoreProperties['storePassword']
        }
      }
      ...
    }
    ...
    
  4. 在通知栏中单击 **立即同步**。

要了解有关应用程序签名的更多信息,请阅读 签署您的应用程序

简化应用程序开发

以下提示有助于使您的 Android 应用程序开发更轻松。

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

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

Groovy

android {
  ...
  buildTypes {
    release {
      // These values are defined only for the release build, which
      // is typically used for full builds and continuous builds.
      buildConfigField("String", "BUILD_TIME", "\"${minutesSinceEpoch}\"")
      resValue("string", "build_time", "${minutesSinceEpoch}")
      ...
    }
    debug {
      // Use static values for incremental builds to ensure that
      // resource files and BuildConfig aren't rebuilt with each run.
      // If these rebuild dynamically, they can interfere with
      // Apply Changes as well as Gradle UP-TO-DATE checks.
      buildConfigField("String", "BUILD_TIME", "\"0\"")
      resValue("string", "build_time", "0")
    }
  }
}
...

Kotlin

android {
  ...
  buildTypes {
    release {
      // These values are defined only for the release build, which
      // is typically used for full builds and continuous builds.
      buildConfigField("String", "BUILD_TIME", "\"${minutesSinceEpoch}\"")
      resValue("string", "build_time", "${minutesSinceEpoch}")
      ...
    }
    debug {
      // Use static values for incremental builds to ensure that
      // resource files and BuildConfig aren't rebuilt with each run.
      // If these rebuild dynamically, they can interfere with
      // Apply Changes as well as Gradle UP-TO-DATE checks.
      buildConfigField("String", "BUILD_TIME", "\"0\"")
      resValue("string", "build_time", "0")
    }
  }
}
...

在您的应用程序代码中,您可以按如下方式访问属性

Kotlin

...
Log.i(TAG, BuildConfig.BUILD_TIME)
Log.i(TAG, getString(R.string.build_time))

Java

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

与清单共享属性

在某些情况下,您可能需要在清单和代码中都声明相同的属性(例如,在为 FileProvider 声明权限时)。与其在多个位置更新相同的属性以反映更改,不如在模块的 build.gradle 文件中定义一个属性,以使其可用于清单和代码,如以下示例所示。要了解更多信息,请阅读 将构建变量注入清单

Groovy

android {
  // For settings specific to a product flavor, configure these properties
  // for each flavor in the productFlavors block.
  defaultConfig {
    // Creates a property for the FileProvider authority.
    def filesAuthorityValue = applicationId + ".files"
    // Creates a placeholder property to use in the manifest.
    manifestPlaceholders =
      [filesAuthority: filesAuthorityValue]
      // Adds a new field for the authority to the BuildConfig class.
      buildConfigField("String",
                       "FILES_AUTHORITY",
                       "\"${filesAuthorityValue}\"")
  }
  ...
}
...

Kotlin

android {
  // For settings specific to a product flavor, configure these properties
  // for each flavor in the productFlavors block.
  defaultConfig {
    // Creates a property for the FileProvider authority.
    val filesAuthorityValue = applicationId + ".files"
    // Creates a placeholder property to use in the manifest.
    manifestPlaceholders["filesAuthority"] = filesAuthorityValue
      // Adds a new field for the authority to the BuildConfig class.
      buildConfigField("String",
                       "FILES_AUTHORITY",
                       "\"${filesAuthorityValue}\"")
  }
  ...
}
...

在您的清单中,按如下方式访问占位符

<manifest>
  ...
  <application>
    ...
    <provider
      android:name="android.support.v4.content.FileProvider"
      android:authorities="${filesAuthority}"
      android:exported="false"
      android:grantUriPermissions="true">
      ...
    </provider>
  </application>
</manifest>

在应用程序代码中访问 FILES_AUTHORITY 字段如下所示

Kotlin

...
val contentUri: Uri = FileProvider.getUriForFile(context, BuildConfig.FILES_AUTHORITY, myFile)

Java

...
Uri contentUri = FileProvider.getUriForFile(getContext(),
  BuildConfig.FILES_AUTHORITY,
  myFile);