Android 11 開發者預覽版現已推出;快來測試并分享您的反饋吧

配置構建變體

本頁以配置 Build 概覽為基礎,向您介紹如何配置構建變體以根據單個項目構建出不同 Build 的應用,以及如何正確管理依賴項和簽名配置。

每種構建變體都代表您可以構建的一個不同的應用版本。例如,您可能希望為應用構建兩個版本,一個是內容有限的免費版本,另一個是包含更多內容的付費版本。您還可以根據 API 級別或其他設備變量,為應用構建以不同設備為目標的不同版本。但是,如果您想根據設備 ABI 或屏幕密度構建不同的版本,則應構建多個 APK

構建變體是 Gradle 使用一組特定規則將在版本類型和產品類型中配置的設置、代碼和資源結合在一起的結果。雖然您無法直接配置構建變體,但可以配置組成它們的版本類型和產品類型。

例如,“demo”產品類型可以指定不同的功能和設備要求(如自定義源代碼、資源和最低 API 級別),而“debug”版本類型則會應用不同的構建和打包設置(如調試選項和簽名密鑰)。生成的構建變體是應用的“demoDebug”版本,并且包括“demo”產品類型、“debug”版本類型和 main/ 源代碼文件集中包含的配置和資源組合。

配置 Build 類型

您可以在 android 塊內的模塊級 build.gradle 文件中創建和配置 Build 類型。當您創建新模塊時,Android Studio 會自動為您創建“debug”Build 類型和“release”Build 類型。雖然“debug”Build 類型沒有顯示在構建配置文件中,但 Android Studio 會使用 debuggable true 配置它。這樣,您就可以在安全的 Android 設備上調試應用,并使用常規調試密鑰庫配置 APK 簽名。

如果要添加或更改某些設置,可以將“debug”Build 類型添加到您的配置中。以下示例為調試 Build 類型指定了 applicationIdSuffix,并配置了一個使用“debug”Build 類型的設置進行初始化的“staging”Build 類型。

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

注意:當您更改構建配置文件時,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 或 Service 注冊。這樣,您可以使用 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

    android {
        ...
        defaultConfig {...}
        buildTypes {
            debug{...}
            release{...}
        }
        // Specifies one flavor dimension.
        

    

注意:要使用 Google Play 中的多 APK 支持分發您的應用,請將同一 applicationId 值分配給所有變體,并為每個變體分配不同的 versionCode。要在 Google Play 中以獨立應用的形式分發應用的不同變體,您需要為每個變體分配一個不同的 applicationId

創建并配置產品類型后,點擊通知欄中的 Sync Now。同步完成后,Gradle 會根據您的 Build 類型和產品類型自動創建構建變體,并根據 <product-flavor><Build-Type> 對其進行命名。例如,如果您創建了“demo”和“full”產品類型,并保留默認的“debug”和“release”Build 類型,則 Gradle 會創建以下構建變體:

  • demoDebug
  • demoRelease
  • fullDebug
  • fullRelease

您可以將構建變體更改為您要構建并運行的任意變體,只需依次轉到 Build > Select Build Variant,然后從下拉菜單中選擇一種變體即可。不過,要開始使用自己的功能和資源自定義每個構建變體,您需要知道如何創建和管理源代碼文件集

將多個產品類型與類型維度結合起來

在某些情況下,您可能需要合并多個產品類型的配置。例如,您可能需要為基于 API 級別的“full”和“demo”產品類型創建不同的配置。為此,您可以使用 Android Plugin for Gradle 創建多組產品特性作為類型維度。在構建應用時,Gradle 會結合使用您定義的每個類型維度的產品類型配置以及 Build 類型配置,以創建最終的構建變體。Gradle 不會將屬于同一類型維度的產品類型組合在一起。

提示:要根據 ABI 和屏幕密度創建不同版本的應用,您應該構建多個 APK,而不要使用產品類型。

以下代碼示例使用 flavorDimensions 屬性來創建“mode”類型維度和“api”類型維度,前者用于對“full”和“demo”產品類型進行分組,后者用于根據 API 級別對產品類型配置進行分組。

    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"
          ...
        }
      }
    }
    ...
    

Gradle 創建的構建變體數量等于每個類型維度中的類型數與您配置的 Build 類型數量的乘積。當 Gradle 為每個構建變體或相應的 APK 命名時,屬于較高優先級類型維度的產品類型會先顯示,然后是屬于較低優先級維度的產品類型,然后是 Build 類型。以上面的構建配置為例,Gradle 使用以下命名方案創建了總共 12 個構建變體:

構建變體:[minApi24, minApi23, minApi21][Demo, Full][Debug, Release]
對應的 APK:app-[minApi24, minApi23, minApi21]-[demo, full]-[debug, release].apk
例如,
構建變體:minApi24DemoDebug
對應的 APK:app-minApi24-demo-debug.apk

除了可以為各個產品類型和構建變體創建源代碼文件集目錄外,您還可以為每個產品類型組合創建源代碼文件集目錄。例如,您可以創建 Java 源文件并將其添加到 src/demoMinApi24/java/ 目錄中,這樣 Gradle 就只會在構建同時對應這兩種產品類型的變體時才使用這些源文件。您為產品類型組合創建的源代碼文件集的優先級高于屬于各個產品類型的源代碼文件集。要詳細了解源代碼文件集以及 Gradle 如何合并資源,請參閱關于如何創建源代碼文件集的部分。

過濾變體

Gradle 會為您配置的產品類型和 Build 類型的每種可能組合創建構建變體。但是,某些構建變體可能并不是您需要的,或者對于您的項目來說沒有意義。您可以通過在模塊級 build.gradle 文件中創建變體過濾器來移除某些構建變體配置。

以上一部分中的構建配置為例,假設您打算讓“demo”版應用僅支持 API 級別 23 和更高版本。您可以使用 variantFilter 代碼塊過濾掉所有將“minApi21”和“demo”產品類型結合在一起的構建變體配置:

    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)
          }
      }
    }
    ...
    

將變體過濾器添加到構建配置,并點擊通知欄中的 Sync Now 后,Gradle 會忽略符合您指定的條件的所有構建變體,并且當您從菜單欄中依次點擊 Build > Select Build Variant(或點擊工具窗口欄中的 Build Variants 圖標 )時,它們不會再顯示在下拉菜單中。

創建源代碼文件集

默認情況下,Android Studio 會為您希望在所有構建變體之間共享的所有內容創建 main/ 源代碼文件集和目錄。但是,您可以創建新的源代碼文件集來精確控制 Gradle 為特定 Build 類型、產品類型(使用類型維度時的產品類型組合)和編譯變體構建和打包的文件。例如,您可以在 main/ 源代碼文件集中定義基本功能,使用產品類型源代碼文件集為不同客戶端更改應用的品牌,或者僅為使用調試 Build 類型的構建變體添加特殊權限和日志記錄功能。

Gradle 要求您以某種類似于 main/ 源代碼文件集的方式組織源代碼文件集文件和目錄。例如,Gradle 要求將“debug”Build 類型特有的 Java 類文件放在 src/debug/java/ 目錄中。

Android Plugin for Gradle 提供了一個有用的 Gradle 任務,向您展示了如何整理每個 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/ 測試源代碼文件集

當您創建新的構建變體時,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 在您為項目添加特定構建變體的新文件時為您創建這些目錄。例如,要為您的“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/)和構建變體的源代碼文件集目錄(如 src/demoDebug/)。此外,您可以創建定位到特定構建變體的測試源代碼文件集(例如 src/androidTestDemoDebug/)。要了解詳情,請轉到測試源代碼文件集

更改默認源代碼文件集配置

如果您未按照 Gradle 預期的默認源代碼文件集文件結構整理源文件(如上文創建源代碼文件集的相關章節中所述),則可以使用 sourceSets 代碼塊更改 Gradle 為源代碼文件集的每個組件收集文件的位置。您無需改變文件的位置,而只需向 Gradle 提供相對于模塊級 build.gradle 文件的路徑,Gradle 應該會在該路徑下找到每個源代碼文件集組件的文件。要了解您可以配置哪些組件,以及是否可以將它們映射到多個文件或目錄,請參閱 Android Plugin for Gradle DSL 參考文檔

以下代碼示例將 app/other/ 目錄中的源文件映射到 main 源代碼文件集的某些組件,并更改 androidTest 源代碼文件集的根目錄。

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

使用源代碼文件集構建

您可以在源代碼文件集目錄中添加只希望針對某些配置打包在一起的代碼和資源。例如,如果您要構建“demoDebug”這個變體(“demo”產品類型和“debug”Build 類型的混合產物),則 Gradle 會查看這些目錄,并為它們指定以下優先級:

  1. src/demoDebug/(構建變體源代碼文件集)
  2. src/debug/(構建類型源代碼文件集)
  3. src/demo/(產品類型源代碼文件集)
  4. src/main/(主源代碼文件集)

注意:如果您結合使用多個產品類型,那么這些產品類型的優先級由它們所屬的類型維度決定。使用 android.flavorDimensions 屬性列出類型維度時,屬于您列出的第一個類型維度的產品類型的優先級高于屬于第二個類型維度的產品類型,依此類推。此外,您為產品類型組合創建的源代碼文件集的優先級高于屬于各個產品類型的源代碼文件集。

上面列出的順序決定了 Gradle 組合代碼和資源時哪個源代碼文件集的優先級更高。由于 demoDebug/ 源代碼文件集目錄可能包含該構建變體特有的文件,因此,如果 demoDebug/ 包含在 debug/ 中也定義了的文件,則 Gradle 會使用 demoDebug/ 源代碼文件集中的文件。類似地,Gradle 會為 Build 類型和產品類型源代碼文件集中的文件提供比 main/ 中的相同文件更高的優先級。在應用以下構建規則時,Gradle 會考慮這種優先級順序:

  • java/ 目錄中的所有源代碼將一起編譯以生成單個輸出。

    注意:對于給定的構建變體,如果 Gradle 遇到兩個或更多個源代碼文件集目錄定義了同一個 Java 類的情況,則會拋出構建錯誤。例如,在構建調試 APK 時,您不能同時定義 src/debug/Utility.javasrc/main/Utility.java。這是因為 Gradle 在構建過程中會查看這兩個目錄并拋出“重復類”錯誤。如果您要為不同的 Build 類型使用不同版本的 Utility.java,則可以讓每個 Build 類型定義各自的文件版本,而不是將其包含在 main/ 源代碼文件集中。

  • 所有清單都將合并為一個清單。優先級將按照上面列出的順序分配。也就是說,Build 類型的清單設置會替換產品類型的清單設置,依此類推。要了解詳情,請參閱清單合并
  • 同樣,values/ 目錄中的文件也會合并在一起。如果兩個文件同名,例如存在兩個 strings.xml 文件,將按照上述列表中的相同順序指定優先級。也就是說,在 Build 類型源代碼文件集的文件中定義的值會重寫在產品類型的同一文件中定義的值,依此類推。
  • res/asset/ 目錄中的資源會打包到一起。如果在兩個或更多個源代碼文件集中定義了同名的資源,將按照上面列表中的順序指定優先級。
  • 最后,在構建 APK 時,Gradle 會為庫模塊依賴項隨附的資源和清單指定最低優先級。

聲明依賴項

您可以為特定構建變體或測試源代碼文件集配置依賴項,方法是在 Implementation 關鍵字之前加上構建變體或測試源代碼文件集的名稱作為前綴,如以下示例所示。

    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 的簽名配置,否則 Gradle 不會為該 Build 的 APK 簽名。您可以使用 Android Studio 輕松創建發布密鑰并為發布 Build 類型簽名

要使用 Gradle 構建配置為您的發布 Build 類型手動配置簽名,請執行以下操作:

  1. 創建一個密鑰庫。密鑰庫是一個包含一組私鑰的二進制文件。您必須將密鑰庫保存在安全可靠的地方。
  2. 創建一個私鑰。私鑰代表將通過應用識別的實體,如個人或公司。
  3. 將簽名配置添加到模塊級 build.gradle 文件中:

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

要生成已簽名的 APK,請從菜單欄中依次選擇 Build > Generate Signed APKapp/build/apk/app-release.apk 中的軟件包現已使用您的發布密鑰進行簽名。

注意:在構建文件中添加發布密鑰和密鑰庫的密碼并不是一種好的安全做法。作為替代方案,您可以將構建文件配置為從環境變量獲取這些密碼,或讓構建流程提示您輸入這些密碼。

要從環境變量獲取這些密碼,請添加以下代碼:

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

要讓構建流程在您要從命令行調用此版本時提示您輸入這些密碼,請添加以下代碼:

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

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

警告:請將密鑰庫和私鑰保存在安全可靠的地方,并確保您為其創建了安全的備份。如果您將應用發布到 Google Play,隨后丟失了為應用簽名的密鑰,那么您將無法發布任何應用更新,因為您必須始終使用相同的密鑰為應用的所有版本簽名。

為 Wear OS 應用簽名

發布 Wear OS 應用時,手表 APK 和可選的手機 APK 都需要進行簽名。要詳細了解如何打包 Wear OS 應用并為其簽名,請參閱打包穿戴式設備應用