配置 build 变体

本页以配置 build 概览为基础,向您介绍如何配置 build 变体以根据单个项目构建出不同 build 的应用,以及如何正确管理依赖项和签名配置。

每种 build 变体都代表您可以构建的一个不同的应用版本。例如,您可能希望为应用构建两个版本,一个是内容有限的免费版本,另一个是包含更多内容的付费版本。您还可以根据 API 级别或其他设备变化因素,为应用构建以不同设备为目标的不同版本。

build 变体是 Gradle 使用一组特定规则将在 build 类型和产品变种中配置的设置、代码和资源组合在一起所得到的结果。虽然您无法直接配置 build 变体,但可以配置组成它们的 build 类型和产品变种。

例如,“demo”产品变种可以指定不同的功能和设备要求(如自定义源代码、资源和最低 API 级别),而“debug”build 类型则会采用不同的构建和打包设置(如调试选项和签名密钥)。生成的 build 变体是应用的“demoDebug”版本,它由“demo”产品变种、“debug”build 类型和 main/ 源代码集中包含的配置和资源组合而成。

配置 build 类型

您可以在模块级 build.gradle 文件中的 android 代码块内创建和配置 build 类型。当您创建新模块时,Android Studio 会自动为您创建“debug”build 类型和“release”build 类型。虽然“debug”build 类型没有出现在 build 配置文件中,但 Android Studio 会使用 debuggable true 配置它。这样,您就可以在安全的 Android 设备上调试应用,并使用常规调试密钥库配置应用签名。

如果您要添加或更改某些设置,可以将“debug”build 类型添加到配置中。以下示例为“debug”build 类型指定了 applicationIdSuffix,并配置了一个使用“debug”build 类型的设置进行初始化的“staging”build 类型。

Groovy

android {
    defaultConfig {
        manifestPlaceholders = [hostName:"www.example.com"]
        ...
    }
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

        debug {
            applicationIdSuffix ".debug"
            debuggable true
        }

        /**
         * The `initWith` property allows you to copy configurations from other build types,
         * then configure only the settings you want to change. This one copies the debug build
         * type, and then changes the manifest placeholder and application ID.
         */
        staging {
            initWith debug
            manifestPlaceholders = [hostName:"internal.example.com"]
            applicationIdSuffix ".debugStaging"
        }
    }
}

Kotlin

android {
    defaultConfig {
        manifestPlaceholders["hostName"] = "www.example.com"
        ...
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
        }

        getByName("debug") {
            applicationIdSuffix = ".debug"
            isDebuggable = true
        }

        /**
         * The `initWith` property allows you to copy configurations from other build types,
         * then configure only the settings you want to change. This one copies the debug build
         * type, and then changes the manifest placeholder and application ID.
         */
        create("staging") {
            initWith(getByName("debug"))
            manifestPlaceholders["hostName"] = "internal.example.com"
            applicationIdSuffix = ".debugStaging"
        }
    }
}

注意:当您对构建配置文件做出更改时,Android Studio 会要求您将项目与新配置同步。如需同步项目,您可以点击做出更改后立即显示的通知栏中的 Sync Now,或点击工具栏中的 Sync Project 图标 。如果 Android Studio 发现配置有任何错误,就会显示 Messages 窗口,以说明相应的问题。

如需详细了解您可以通过 build 类型配置的所有属性,请参阅 build 类型 DSL 参考文档

配置产品变种

创建产品变种与创建 build 类型类似:将其添加到构建配置中的 productFlavors 代码块并添加所需的设置。产品变种支持与 defaultConfig 相同的属性,这是因为,defaultConfig 实际上属于 ProductFlavor 类。这意味着,您可以在 defaultConfig 代码块中提供所有变种的基本配置,每个变种均可更改其中任何默认值,如 applicationId。如需详细了解应用 ID,请参阅设置应用 ID

注意:您仍然需要在 main/ 清单文件中使用 package 属性指定软件包名称。此外,您还必须在源代码中使用该软件包名称引用 R 类,或解析任何相关的 Activity 或服务注册。这样,您就可以使用 applicationId 为每个产品变种指定一个唯一的 ID,以用于打包和分发,而不必更改源代码。

所有变种都必须属于一个指定的变种维度,即一个产品变种组。您必须将所有变种分配给某个变种维度;否则,您将收到如下所示的构建错误。如果给定的模块仅指定一个变种维度,那么 Android Gradle 插件会自动将该模块的所有变种分配给该维度。

  Error:All flavors must now belong to a named flavor dimension.
  The flavor 'flavor_name' is not assigned to a flavor dimension.

以下代码示例创建了一个名为“version”的变种维度,并添加了“demo”和“full”产品变种。这些变种提供了它们自己的 applicationIdSuffixversionNameSuffix

Groovy

android {
    ...
    defaultConfig {...}
    buildTypes {
        debug{...}
        release{...}
    }
    // Specifies one flavor dimension.
    flavorDimensions "version"
    productFlavors {
        demo {
            // Assigns this product flavor to the "version" flavor dimension.
            // If you are using only one dimension, this property is optional,
            // and the plugin automatically assigns all the module's flavors to
            // that dimension.
            dimension "version"
            applicationIdSuffix ".demo"
            versionNameSuffix "-demo"
        }
        full {
            dimension "version"
            applicationIdSuffix ".full"
            versionNameSuffix "-full"
        }
    }
}

Kotlin

android {
    ...
    defaultConfig {...}
    buildTypes {
        getByName("debug"){...}
        getByName("release"){...}
    }
    // Specifies one flavor dimension.
    flavorDimensions "version"
    productFlavors {
        create("demo") {
            // Assigns this product flavor to the "version" flavor dimension.
            // If you are using only one dimension, this property is optional,
            // and the plugin automatically assigns all the module's flavors to
            // that dimension.
            dimension = "version"
            applicationIdSuffix = ".demo"
            versionNameSuffix = "-demo"
        }
        create("full") {
            dimension = "version"
            applicationIdSuffix = ".full"
            versionNameSuffix = "-full"
        }
    }
}

注意:对于使用 APK 在 Google Play 上进行分发的旧版应用(创建于 2021 年 8 月之前),如需在 Google Play 中使用多 APK 支持分发应用,请将相同的 applicationId 值赋予所有变体,并为每个变体分别指定一个不同的 versionCode。如需在 Google Play 中以独立应用的形式分发应用的不同变体,您需要为每个变体分配一个不同的 applicationId

创建并配置产品变种后,点击通知栏中的 Sync Now。同步完成后,Gradle 会根据 build 类型和产品变种自动创建 build 变体,并按照 <product-flavor><Build-Type> 为其命名。例如,如果您创建了“demo”和“full”产品变种,并保留了默认的“debug”和“release”build 类型,则 Gradle 会创建以下 build 变体:

  • demoDebug
  • demoRelease
  • fullDebug
  • fullRelease

您可以将 build 变体更改为您要构建并运行的任意变体,只需依次转到 Build > Select Build Variant,然后从下拉菜单中选择一个变体即可。不过,如需开始使用每个 build 变体自己的功能和资源对其进行自定义,您需要知道如何创建和管理源代码集

更改 build 变体的应用 ID

当您为应用构建 APK 或 AAB 时,构建工具会使用 build.gradle 文件的 defaultConfig 块中定义的应用 ID 标记应用(如下所示)。不过,如果您要创建不同版本的应用,让其在 Google Play 商店中显示为单独的详情(如“free”和“pro”版本),就需要创建单独的 build 变体,这些变体各自具有不同的应用 ID。 在这种情况下,每个 build 变体应定义为单独的产品变种。对于 productFlavors 块中的每个变种,您可以重新定义 applicationId 属性,也可以使用 applicationIdSuffix 在默认的应用 ID 上追加一段,如下所示:

Groovy

android {
    defaultConfig {
        applicationId "com.example.myapp"
    }
    productFlavors {
        free {
            applicationIdSuffix ".free"
        }
        pro {
            applicationIdSuffix ".pro"
        }
    }
}

Kotlin

android {
    defaultConfig {
        applicationId = "com.example.myapp"
    }
    productFlavors {
        create("free") {
            applicationIdSuffix = ".free"
        }
        create("pro") {
            applicationIdSuffix = ".pro"
        }
    }
}
这样,“free”产品变种的应用 ID 就是“com.example.myapp.free”。 您也可以根据自己的 build 类型使用 applicationIdSuffix 追加一段,如下所示:

Groovy

android {
    ...
    buildTypes {
        debug {
            applicationIdSuffix ".debug"
        }
    }
}

Kotlin

android {
    ...
    buildTypes {
        getByName("debug") {
            applicationIdSuffix = ".debug"
        }
    }
}
由于 Gradle 会在产品变种后面应用 build 类型配置,因此“free debug”build 变体的应用 ID 现在是“com.example.myapp.free.debug”。如果您希望同一设备上同时具有调试 build 和发布 build,这会很有用,因为两个应用不能具有相同的应用 ID。 对于使用 APK 在 Google Play 上进行分发的旧版应用(创建于 2021 年 8 月之前),如果您想使用相同的应用详情分发多个 APK,并让每个 APK 都以不同的设备配置(例如 API 级别)作为目标,则您必须对所有 build 变体使用相同的应用 ID,但为每个 APK 分别指定一个不同的 versionCode。如需了解详情,请参阅多 APK 支持。如果您使用 AAB 进行发布,则不会受到任何影响,因为 AAB 使用单个工件,而该工件默认使用单个版本代码和应用 ID。

注意:为了与以前的 SDK 工具兼容,如果您未在 build.gradle 文件中定义 applicationId 属性,构建工具会将 AndroidManifest.xml 文件中的软件包名称用作应用 ID。在这种情况下,重构您的软件包名称也会更改您的应用 ID。

提示:如果需要在清单文件中引用应用 ID,您可以在任何清单属性中使用 ${applicationId} 占位符。在构建期间,Gradle 会将此标记替换为实际的应用 ID。如需了解详情,请参阅将 build 变量注入清单

将多个产品变种与变种维度组合在一起

在某些情况下,您可能要将多个产品变种的配置组合在一起。例如,您可能要为基于 API 级别的“full”和“demo”产品变种创建不同的配置。为此,您可以使用 Android Plugin for Gradle 创建多组产品变种作为变种维度。在构建应用时,Gradle 会将您定义的每个变种维度中的产品变种配置以及 build 类型配置组合在一起,以创建最终的 build 变体。Gradle 不会将属于同一变种维度的产品变种组合在一起。

以下代码示例使用 flavorDimensions 属性创建“mode”变种维度和“api”变种维度,前者用于对“full”和“demo”产品变种进行分组,后者用于根据 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("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"
      minSdk = 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"
      minSdk = 23
      versionCode = 20000  + android.defaultConfig.versionCode
      versionNameSuffix = "-minApi23"
      ...
    }

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

Gradle 创建的 build 变体数量等于每个变种维度中的变种数量与您配置的 build 类型数量的乘积。当 Gradle 为每个 build 变体或对应的工件命名时,先显示属于较高优先级变种维度的产品变种,接着是较低优先级维度中的产品变种,再接着是 build 类型。以上面的 build 配置为例,Gradle 使用以下命名方案创建了总共 12 个 build 变体:

build 变体:[minApi24, minApi23, minApi21][Demo, Full][Debug, Release]
对应的 APK:app-[minApi24, minApi23, minApi21]-[demo, full]-[debug, release].apk
例如,
build 变体:minApi24DemoDebug
对应的 APK:app-minApi24-demo-debug.apk

除了可以为各个产品变种和 build 变体创建源代码集目录之外,您还可以为产品变种的每个组合创建源代码集目录。例如,您可以创建 Java 源代码并将其添加到 src/demoMinApi24/java/ 目录中,只有在构建将这两个产品变种组合在一起的变体时,Gradle 才会使用这些源代码。您为产品变种组合创建的源代码集的优先级高于属于各个产品变种的源代码集。如需详细了解源代码集以及 Gradle 如何合并资源,请参阅关于如何创建源代码集的部分。

过滤变体

Gradle 会为您配置的产品变种和 build 类型的每种可能组合创建一个 build 变体。不过,某些 build 变体可能并不是您需要的,或者对于您的项目来说没有意义。您可以在模块级 build.gradle 文件中创建变体过滤器,以移除某些 build 变体配置。

以上一部分中的 build 配置为例,假设您打算让“demo”版应用仅支持 API 级别 23 及更高级别。您可以使用 variantFilter 代码块过滤掉所有将“minApi21”和“demo”产品变种组合在一起的 build 变体配置:

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.addAll(listOf("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
        }
    }
}
...

将变体过滤器添加到 build 配置并点击通知栏中的 Sync Now 后,Gradle 会忽略符合您指定的条件的所有 build 变体,并且当您从菜单栏中依次点击 Build > Select Build Variant(或点击工具窗口栏中的 Build Variants 图标 )时,它们不会再显示在下拉菜单中。

创建源代码集

默认情况下,Android Studio 会为您希望在所有 build 变体之间共享的所有内容创建 main/ 源代码集和目录。不过,您可以创建新的源代码集以精确控制 Gradle 为特定 build 类型、产品变种(以及使用变种维度时的产品变种组合)和 build 变体编译和打包的文件。例如,您可以在 main/ 源代码集中定义基本功能,并使用产品变种源代码集为不同客户端更改应用的品牌信息,或仅为使用“debug”build 类型的 build 变体添加特殊权限和日志记录功能。

Gradle 要求您以某种类似于 main/ 源代码集的方式组织源代码集文件和目录。例如,Gradle 要求将“debug”build 类型特有的 Java 类文件放在 src/debug/java/ 目录中。

Android Plugin for Gradle 提供了一项有用的 Gradle 任务,向您展示了如何组织每个 build 类型、产品变种和 build 变体的文件。例如,以下任务输出示例描述了 Gradle 要求在何处能够找到“debug”build 类型的某些文件:

------------------------------------------------------------
Project :app
------------------------------------------------------------

...

debug
----
Compile configuration: compile
build.gradle name: android.sourceSets.debug
Java sources: [app/src/debug/java]
Manifest file: app/src/debug/AndroidManifest.xml
Android resources: [app/src/debug/res]
Assets: [app/src/debug/assets]
AIDL sources: [app/src/debug/aidl]
RenderScript sources: [app/src/debug/rs]
JNI sources: [app/src/debug/jni]
JNI libraries: [app/src/debug/jniLibs]
Java-style resources: [app/src/debug/resources]

如需查看此输出,请按以下步骤操作:

  1. 点击 IDE 窗口右侧的 Gradle 图标
  2. 依次转到 MyApplication > Tasks > android,然后双击 sourceSets。Gradle 执行该任务后,系统应该会打开 Run 窗口以显示输出。
  3. 如果显示内容不是处于如上所示的文本模式,请点击 Run 窗口左侧的 Toggle view 图标

注意:任务输出还为您展示了如何组织要用于运行应用测试的文件的源代码集,例如 test/androidTest/ 测试源代码集

当您创建新的 build 变体时,Android Studio 不会为您创建源代码集目录,而是为您提供一些有用的选项。例如,如需仅为“debug”build 类型创建 java/ 目录,请执行以下操作:

  1. 打开 Project 窗格,然后从窗格顶部的下拉菜单中选择 Project 视图。
  2. 转到 MyProject/app/src/
  3. 右键点击 src 目录,然后依次选择 New > Folder > Java Folder
  4. Target Source Set 旁边的下拉菜单中,选择 debug
  5. 点击 Finish

Android Studio 会为“debug”build 类型创建源代码集目录,然后在其中创建 java/ 目录。或者,您也可以让 Android Studio 在您为特定 build 变体向项目中添加新文件时为您创建这些目录。例如,如需为“debug”build 类型创建值 XML 文件,请执行以下操作:

  1. 在同一 Project 窗格中,右键点击 src 目录,然后依次选择 New > XML > Values XML File
  2. 输入 XML 文件的名称或保留默认名称。
  3. Target Source Set 旁边的下拉菜单中,选择 debug
  4. 点击 Finish

由于“debug”build 类型被指定为目标源代码集,因此 Android Studio 会在创建 XML 文件时自动创建必要的目录。生成的目录结构应如图 2 所示。

图 2. “debug”build 类型的新源代码集目录。

遵循相同的过程,您也可以创建产品变种的源代码集目录(如 src/demo/)和 build 变体的源代码集目录(如 src/demoDebug/)。此外,您还可以创建针对特定 build 变体的测试源代码集,如 src/androidTestDemoDebug/。如需了解详情,请参阅测试源代码集一文。

更改默认源代码集配置

如果您的源代码未按照 Gradle 要求的默认源代码集文件结构进行组织(如上文关于创建源代码集的部分中所述),您可以使用 sourceSets 代码块更改 Gradle 为源代码集的每个组件收集文件的位置。您无需改变文件的位置,只需向 Gradle 提供相对于模块级 build.gradle 文件的路径,Gradle 应该会在该路径下找到每个源代码集组件的文件。如需了解您可以配置哪些组件,以及是否可以将它们映射到多个文件或目录,请参阅 Android Gradle 插件 API 参考文档

以下代码示例将 app/other/ 目录中的源代码映射到了 main 源代码集的某些组件,并更改了 androidTest 源代码集的根目录。

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']

      // If you list multiple directories, Gradle uses all of them to collect
      // sources. Because Gradle gives these directories equal priority, if
      // you define the same resource in more than one directory, you get an
      // error when merging resources. The default directory is 'src/main/res'.
      res.srcDirs = ['other/res1', 'other/res2']

      // Note: You should avoid specifying a directory which is a parent to one
      // or more other directories you specify. For example, avoid the following:
      // res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings']
      // You should specify either only the root 'other/res1' directory, or only the
      // nested 'other/res1/layouts' and 'other/res1/strings' directories.

      // For each source set, you can specify only one Android manifest.
      // By default, Android Studio creates a manifest for your main source
      // set in the src/main/ directory.
      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.
    main {
      // Changes the directory for Java sources. The default directory is
      // 'src/main/java'.
      java.setSrcDirs("other/java")

      // If you list multiple directories, Gradle uses all of them to collect
      // sources. Because Gradle gives these directories equal priority, if
      // you define the same resource in more than one directory, you get an
      // error when merging resources. The default directory is 'src/main/res'.
      res.setSrcDirs("other/res1", "other/res2")

      // Note: You should avoid specifying a directory which is a parent to one
      // or more other directories you specify. For example, avoid the following:
      // res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings']
      // You should specify either only the root 'other/res1' directory, or only the
      // nested 'other/res1/layouts' and 'other/res1/strings' directories.

      // For each source set, you can specify only one Android manifest.
      // By default, Android Studio creates a manifest for your main source
      // set in the src/main/ directory.
      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")
      ...
    }
  }
}
...

使用源代码集构建

您可以让源代码集目录包含您希望只针对某些配置打包在一起的代码和资源。例如,如果您要构建“demoDebug”这个变体(“demo”产品变种和“debug”build 类型的混合产物),则 Gradle 会查看这些目录,并为它们指定以下优先级:

  1. src/demoDebug/(build 变体源代码集)
  2. src/debug/(build 类型源代码集)
  3. src/demo/(产品变种源代码集)
  4. src/main/(主源代码集)

注意:如果您将多个产品变种组合在一起,那么这些产品变种的优先级由它们所属的变种维度决定。使用 android.flavorDimensions 属性列出变种维度时,属于您列出的第一个变种维度的产品变种的优先级高于属于第二个变种维度的产品变种,依此类推。此外,您为产品变种组合创建的源代码集的优先级高于属于各个产品变种的源代码集。

上面列出的顺序决定了 Gradle 组合代码和资源时哪个源代码集的优先级更高。由于 demoDebug/ 源代码集目录很可能包含该 build 变体特有的文件,因此如果 demoDebug/ 包含的某个文件在 debug/ 中也进行了定义,Gradle 会使用 demoDebug/ 源代码集中的文件。同样,Gradle 会为 build 类型和产品变种源代码集中的文件指定比 main/ 中的相同文件更高的优先级。在应用以下构建规则时,Gradle 会考虑这种优先级顺序:

  • java/ 目录中的所有源代码将一起编译以生成单个输出。

    注意:对于给定的 build 变体,如果 Gradle 遇到两个或更多个源代码集目录定义了同一个 Java 类的情况,就会抛出构建错误。例如,在构建调试应用时,您不能同时定义 src/debug/Utility.javasrc/main/Utility.java。这是因为,Gradle 在构建过程中会查看这两个目录并抛出“重复类”错误。如果您希望不同的 build 类型有不同版本的 Utility.java,就可以让每个 build 类型定义各自的文件版本,而不是将其包含在 main/ 源代码集中。

  • 所有清单都将合并为一个清单。将按照上面列表中的顺序指定优先级。也就是说,build 类型的清单设置会替换产品变种的清单设置,依此类推。如需了解详情,请参阅清单合并
  • 同样,values/ 目录中的文件也会合并在一起。如果两个文件同名,例如存在两个 strings.xml 文件,将按照上面列表中的顺序指定优先级。也就是说,在 build 类型源代码集的文件中定义的值会替换在产品变种的同一文件中定义的值,依此类推。
  • res/asset/ 目录中的资源会打包在一起。如果在两个或更多个源代码集中定义了同名的资源,将按照上面列表中的顺序指定优先级。
  • 最后,在构建应用时,Gradle 会为库模块依赖项随附的资源和清单指定最低优先级。

声明依赖项

您可以为特定 build 变体或测试源代码集配置依赖项,方法是在 Implementation 关键字前面加上 build 变体或测试源代码集的名称作为前缀,如以下示例所示。

Groovy

dependencies {
    // Adds the local "mylibrary" module as a dependency to the "free" flavor.
    freeImplementation project(":mylibrary")

    // 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'
}

Kotlin

dependencies {
    // Adds the local "mylibrary" module as a dependency to the "free" flavor.
    freeImplementation(project(":mylibrary"))

    // 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")
}

如需了解详情,请参阅添加 build 依赖项

使用变体感知型依赖项管理机制

Android 插件 3.0.0 及更高版本包含一种新的依赖项机制,该机制可在使用库时自动匹配变体。这意味着,应用的 debug 变体会自动使用库的 debug 变体,依此类推。在使用变种时(这时应用的 freeDebug 变体将使用库的 freeDebug 变体),这种机制也同样适用。

为了让插件准确匹配变体,您需要在无法进行直接匹配的情况下提供匹配回退机制。不妨假设您的应用配置了一个名为“staging”的 build 类型,但该应用的一个库依赖项没有进行相应配置。当插件尝试构建“staging”版本的应用时,它不知道要使用哪个版本的库,因此您将看到一条与以下内容类似的错误消息:

Error:Failed to resolve: Could not resolve project :mylibrary.
Required by:
    project :app

解决与变体匹配相关的构建错误

插件包含一些 DSL 元素,这些元素有助于控制 Gradle 应如何解决应用与依赖项之间无法进行直接变体匹配的问题。请参阅下表,以确定应使用哪个 DSL 属性来解决与变体感知依赖项匹配相关的特定构建错误。

构建错误原因 解决方案

您的应用包含库依赖项不包含的 build 类型

例如,您的应用包含“staging”build 类型,但依赖项仅包含“debug”和“release”build 类型。

请注意,如果库依赖项包含您的应用不包含的 build 类型,这不会引发问题。这是因为,插件在任何时候都不会从依赖项请求该 build 类型。

使用 matchingFallbacks 为给定的 build 类型指定替代匹配项,如下所示:

Groovy


// 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']
        }
    }
}

Kotlin


// In the app's build.gradle file.
android {
    buildTypes {
        getByName("debug") {}
        getByName("release") {}
        create("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 += listOf("debug", "qa", "release")
        }
    }
}

对于应用及其库依赖项中均存在的给定变种维度,您的应用包含库不包含的变种

例如,您的应用及其库依赖项都包含“tier”变种维度。不过,应用中的“tier”维度包含“free”和“paid”变种,但依赖项中的同一维度仅包含“demo”和“paid”变种。

请注意,对于应用及其库依赖项中均存在的给定变种维度,如果库包含您的应用不包含的产品变种,这不会引发问题。这是因为,插件在任何时候都不会从依赖项请求该变种。

使用 matchingFallbacks 为应用的“free”产品变种指定替代匹配项,如下所示:

Groovy


// 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']
        }
    }
}

Kotlin


// 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 += listOf("tier")
    productFlavors {
        create("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.
        }
        create("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 += listOf("demo", "trial")
        }
    }
}

库依赖项包含您的应用不包含的变种维度。

例如,库依赖项包含“minApi”维度的变种,但您的应用仅包含“tier”维度的变种。因此,当您要构建“freeDebug”版本的应用时,插件不知道是使用“minApi23Debug”还是“minApi18Debug”版本的依赖项。

请注意,如果您的应用包含库依赖项不包含的变种维度,这不会引发问题。这是因为,插件只会匹配依赖项中存在的维度的变种。例如,如果依赖项不包含 ABI 的维度,“freeX86Debug”版本的应用将直接使用“freeDebug”版本的依赖项。

defaultConfig 代码块中使用 missingDimensionStrategy 指定插件应从每个缺失维度中选择的默认变种,如以下示例所示。您也可以替换在 productFlavors 代码块中的选择,让每一个变种都可以为缺失维度指定一个不同的匹配策略。

Groovy


// 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 {}
    }
}

Kotlin


// 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 {
        create("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")
        }
        create("paid") {}
    }
}

如需了解详情,请参阅 Android 插件 DSL 参考文档中的 matchingFallbacksmissingDimensionStrategy

配置签名设置

除非您明确定义发布 build 的签名配置,否则 Gradle 不会为该 build 的 APK 或 AAB 文件签名。如果您还没有签名密钥,请参阅介绍如何使用 Android Studio 生成上传密钥和密钥库的内容。

如需使用 Gradle build 配置为“release”build 类型手动进行签名配置,请执行以下操作:

  1. 创建一个密钥库。密钥库是一个包含一组私钥的二进制文件。您必须将密钥库保存在安全可靠的地方。
  2. 创建一个私钥。私钥用于为应用签名以进行分发,绝不能包含在应用中,也不得透露给未经授权的第三方。
  3. 将签名配置添加到模块级 build.gradle 文件中:

    Groovy

    ...
    android {
        ...
        defaultConfig {...}
        signingConfigs {
            release {
                storeFile file("myreleasekey.keystore")
                storePassword "password"
                keyAlias "MyReleaseKey"
                keyPassword "password"
            }
        }
        buildTypes {
            release {
                ...
                signingConfig signingConfigs.release
            }
        }
    }

    Kotlin

    ...
    android {
        ...
        defaultConfig {...}
        signingConfigs {
            create("release") {
                storeFile = file("myreleasekey.keystore")
                storePassword = "password"
                keyAlias = "MyReleaseKey"
                keyPassword = "password"
            }
        }
        buildTypes {
            getByName("release") {
                ...
                signingConfig = signingConfigs.getByName("release")
            }
        }
    }

注意:在 build 文件中添加发布密钥和密钥库的密码并不是一种好的安全做法。作为替代方案,您可以将构建文件配置为从环境变量获取这些密码,或让构建流程提示您输入这些密码。

如需从环境变量获取这些密码,请添加以下代码:

Groovy

storePassword System.getenv("KSTOREPWD")
keyPassword System.getenv("KEYPWD")

Kotlin

storePassword = System.getenv("KSTOREPWD")
keyPassword = System.getenv("KEYPWD")

如需让构建流程在您要从命令行调用 build 时提示您输入这些密码,请添加以下代码:

Groovy

storePassword System.console().readLine("\nKeystore password: ")
keyPassword System.console().readLine("\nKey password: ")

Kotlin

storePassword = System.console().readLine("\nKeystore password: ")
keyPassword = System.console().readLine("\nKey password: ")

完成此流程后,您可以分发您的应用并在 Google Play 上发布它。

警告:请将密钥库和私钥保存在安全可靠的地方,并确保您为其创建了安全的备份。如果您使用了 Play 应用签名功能,那么当您丢失了上传密钥时,您可以通过 Play 管理中心请求重置该密钥。 如果您在未启用 Play 应用签名功能的情况下发布应用(适用于 2021 年 8 月之前创建的应用),而您又丢失了应用签名密钥,那么您将无法发布任何应用更新,因为您必须始终使用相同的密钥为应用的所有版本签名。

为 Wear OS 应用签名

发布 Wear OS 应用时,需要为手表 APK 和可选的手机 APK 签名。如需详细了解如何打包 Wear OS 应用并为其签名,请参阅打包和分发 Wear 应用