設定建構變數

透過集合功能整理內容 你可以依據偏好儲存及分類內容。

本頁面參考設定建構總覽,說明如何設定建構變數,以便透過單一專案建立不同版本的應用程式,還說明如何正確管理依附元件和簽署設定。

每個建構變數都代表可以建構的不同應用程式版本。例如,您可能想建構兩個版本的應用程式:一個是包含有限內容的免費版應用程式,另一個則是包含更多內容的付費版應用程式。您也可以根據 API 級別或其他裝置變化版本,建構針對不同裝置的不同變體的應用程式。

建構變數是 Gradle 透過一組特定規則結合建構類型和變種版本中設定的各項設定、程式碼及資源後得到的結果。雖然您並未直接設定建構變數,但必須設定組成建構變數所需的建構類型和變種版本。

舉例來說,「試用版」變種版本可以指定不同的功能和裝置需求,例如自訂原始碼、資源和最低 API 級別,而「偵錯」建構類型會套用不同的建構與封裝設定,例如偵錯選項和簽署金鑰。產生的建構變化版本是「demoDebug」應用程式的版本,其中包含「試用版」變種版本、偵錯版本類型和 main/ 來源集。

設定建構類型

您可以在 android 區塊的模組層級 build.gradle 檔案中建立及設定建構類型。在您建立新的模組時,Android Studio 會自動為您建立偵錯和發布子版本類型。雖然偵錯版本類型不會顯示在建構設定檔中,但 Android Studio 是以 debuggable true 來設定。讓您能在安全的 Android 裝置上對應用程式進行偵錯,並使用一般偵錯 KeyStore 設定應用程式簽署。

如果您想新增或變更某些設定,可以在設定中加入「偵錯」建構類型。下列範例說明如何為偵錯建構類型指定 applicationIdSuffix,並設定使用偵錯建構類型中的設定初始化的「測試環境」建構類型。

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) 視窗來說明問題。

如要進一步瞭解可使用建構類型設定的所有屬性,請參閱 BuildType DSL 參考資料。

設定變種版本

變種版本的建立方法與建構類型類似,只需將變種版本新增到建構設定中的 productFlavors 區塊,並包含所需設定即可。 變種版本支援與 defaultConfig 相同的屬性,因為 defaultConfig 實際上是屬於 ProductFlavor 類別。也就是說,您可以在 defaultConfig 區塊中針對所有情境提供基本設定,並為每個版本變更任何預設值,例如 applicationId。如要進一步瞭解應用程式 ID,請參閱設定應用程式 ID

注意事項:您仍然需要使用 main/ 資訊清單檔案中的 package 屬性來指定套件名稱,也必須在原始碼中使用該套件名稱來參照 R 類別,或解析任何相關活動或服務註冊。因此,使用 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」的版本維度,並新增「試用版」和「完整版」變種版本。下列版本提供各自專屬的 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 月前建立),要透過支援多個 APK 在 Google Play 發行應用程式,請為所有變化版本指派相同的 applicationId 值,並分別指定不同的 versionCode。如要在 Google Play 中以獨立應用程式發行不同變化版本的應用程式,您需要為每個變化版本指派不同的 applicationId

建立及設定變種版本之後,請按一下通知列中的「Sync Now」(立即同步)。同步處理完成後,Gradle 會根據您的建構類型和變種版本自動建立建構變數,並根據 <product-flavor><Build-Type> 為其命名。舉例來說,假設您建立了「試用版」和「完整版」變種版本,並保留預設的「偵錯」和「發布」建構類型,Gradle 會建立下列建構變數:

  • demoDebug
  • demoRelease
  • fullDebug
  • fullRelease

您可以將建構變數變更為您要建構及執行的任何變數,方法是依序前往「Build」(建構) >「Select Build Variant」(選取建構變數),然後從下拉式選單中選取一個變數。不過,如要開始使用各個建構變數專屬的功能和資源進行自訂,您必須瞭解如何建立及管理來源集

變更建構變數的應用程式 ID

您為應用程式建構 APK 或 AAB 時,建構工具會標記應用程式 ID 與 build.gradle 檔案 defaultConfig 區塊中定義的應用程式 ID (如下所示)。不過,如果您想要建立不同版本的應用程式,讓新版本在 Google Play 商店中顯示為個別產品 (例如「免付費」和「專業」版本),就必須分別建立構建變數,每個都具備不同的應用程式 ID。在這種情況下,每個建構變數都應該定義為獨立的變種版本。您可以針對 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"
        }
    }
}
這樣一來,「免付費」變種版本的應用程式 ID 為「com.example.myapp.free」。您也可以根據建構類型,使用 applicationIdSuffix 附加區隔,如下所示:

Groovy

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

Kotlin

android {
    ...
    buildTypes {
        getByName("debug") {
            applicationIdSuffix = ".debug"
        }
    }
}
由於 Gradle 會在變種版本之後套用建構類型設定,因此「免費偵錯」建構變數的應用程式 ID 現在是「com.example.myapp.free.debug」。如果您希望同一部裝置同時具有「偵錯」和「發布」兩個版本類型,這項功能就非常實用,因為兩個應用程式無法具有相同的應用程式 ID。如果是以 APK 格式在 Google Play 發行的舊版應用程式 (2021 年 8 月前建立),當您想使用相同的應用程式資訊發行多種 APK,且每個 APK 以不同的裝置設定 (例如 API 級別) 做為目標,那麼您必須為每個建構變數使用相同的應用程式 ID,但須為每個 APK 分別指定不同的 versionCode。詳情請參閱多重 APK 支援。根據預設,使用 AAB 發布的內容並不會受影響,因為其使用單一成果,預設情況下使用單一版本程式碼和應用程式 ID。

注意:如要與先前的 SDK 工具相容,如果您未在 build.gradle 檔案中定義 applicationId 屬性,建構工具會使用 AndroidManifest.xml 檔案中的套件名稱做為應用程式 ID。在這種情況下,重構套件名稱也會變更應用程式 ID。

提示:如需在資訊清單檔案中參照應用程式 ID,您可以在任何資訊清單屬性中使用 ${applicationId} 預留位置。在建構期間,Gradle 會將標記替換為實際的應用程式 ID。詳情請參閱將建構變數插入資訊清單

結合多種變種版本和版本維度

在某些情況下,您可能會想結合多種變種版本的設定。舉例來說,您可能想根據 API 層級為「完整版」和「試用版」變種版本建立不同的設定。如要這麼做,您可以使用 Gradle 適用的 Android 外掛程式建立多組變種版本做為版本維度。建構應用程式時,Gradle 會結合您定義的各種版本維度中的變種版本設定,以及建構類型設定,以建立最終的建構變數。Gradle 不會結合同一版本維度中的不同變種版本。

下列程式碼範例使用 flavorDimensions 屬性建立「mode」版本維度,將「完整版」和「試用版」變種版本分組,以及建立「api」版本維度,根據 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"
      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 ?: 0)
      versionNameSuffix = "-minApi24"
      ...
    }

    create("minApi23") {
      dimension = "api"
      minSdk = 23
      versionCode = 20000  + (android.defaultConfig.versionCode ?: 0)
      versionNameSuffix = "-minApi23"
      ...
    }

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

Gradle 建立的建構變數數量等於每個版本維度中的變種版本與您設定的建構類型數量的乘積。當 Gradle 為每個建構變數或對應的構件命名時,系統會依照版本維度的優先順序,從高到低依序列出其中的變種版本,然後是建構類型。以上面的建構設定為例,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.gradle 檔案中建立變數篩選器,藉此移除特定建構變數設定。

以上一節中的建構設定為例,假設您計劃僅支援試用版應用程式的 API 級別 23 和更高級別,就可以使用 variantFilter 區塊,篩除所有結合「minApi21」和「試用版」變種版本的建構變數設定:

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 += 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
        }
    }
}
...

在建構設定中新增變數篩選器,並按一下通知列中的「Sync Now」(立即同步)後,Gradle 會忽略所有符合指定條件的建構變數,而且當您在選單列依序按一下「Build」(建構) >「Select Build Variant」(選取建構變數) (或在工具視窗列按一下「Build Variants」(建構變數) 圖示) 時,這些變數不會再顯示於下拉式選單。

建立來源集

根據預設,Android Studio 會建立 main/來源集和目錄,供您在所有建構變化版本中共用。不過,您可以建立新的來源集,以確切控制 Gradle 為特定建構類型、變種版本 (以及使用版本維度時的變種版本組合) 和建構變數編譯和封裝的檔案。例如,您可以在 main/ 來源集中定義基本功能,並使用變種版本來源集變更不同用戶端的應用程式品牌資訊,或只針對使用「偵錯」建構類型的建構變數,加入特殊權限和記錄功能。

Gradle 預期您會以某些方式整理來源集檔案和目錄,與 main/ 來源集相似。例如,Gradle 會預期「偵錯」建構類型專屬的 Java 類別檔案位於 src/debug/java/ 目錄中。

Gradle 適用的 Android 外掛程式提供實用的 Gradle 工作,說明如何為各個建構類型、變種版本和建構變數彙整檔案。例如,下列工作輸出中的範例說明 Gradle 希望可以在哪裡找到「偵錯」建構類型的特定檔案:

------------------------------------------------------------
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」(來源集)。如要查看「Tasks」資料夾,您必須允許 Gradle 在同步處理期間建立工作清單,只需依序按一下「File」>「Settings」>「Experimental」,然後取消勾選「Do not build Gradle task list during Gradle sync」(如果是 macOS 作業系統,則依序按一下「Android Studio」>「Preferences」>「Experimental」)。 Gradle 執行工作之後,「Run」(執行) 視窗應該會開啟以顯示輸出內容。
  3. 如果並非以文字模式顯示 (如上所示),請按一下「Run」(執行) 視窗左側的「Toggle view」(切換檢視) 圖示

注意事項:工作輸出也會說明如何為用於執行應用程式測試的檔案彙整來源集,例如 test/androidTest/ 測試來源集

建立新的建構變數時,Android Studio 不會為您建立來源集目錄,但會為您提供幾個實用選項。例如,僅為「偵錯」構建類型建立 java/ 目錄:

  1. 開啟「Project」(專案) 窗格,然後從窗格頂端的下拉式選單中選取「Project」(專案) 檢視畫面。
  2. 前往 MyProject/app/src/
  3. src 目錄上按一下滑鼠右鍵,然後依序選取「New」>「Directory」
  4. 從「Gradle Source Sets」下方的選單中選取 [full/java]
  5. 按下 Enter 鍵

Android Studio 會為您的「偵錯」版本類型建立來源集目錄,然後在其中建立 java/ 目錄。或者,您也可以在專案中新增特定建構變化版本的檔案,讓 Android Studio 為您建立目錄。例如,如要為「偵錯」建構類型建立值的 XML 檔案:

  1. 在同一個「Project」(專案) 窗格的 src 目錄按一下滑鼠右鍵,然後依序選取「New」(新增) >「XML」>「Values XML File」(值 XML 檔案)
  2. 輸入 XML 檔案名稱,或是保留預設名稱。
  3. 在「Target Source Set」(目標來源集) 旁的下拉式選單中,選取「debug」(偵錯)
  4. 按一下「Finish」(完成)

由於「偵錯」建構類型是目標來源集,因此 Android Studio 會在建立 XML 檔案時自動建立必要的目錄。產生的目錄結構應如圖 2 所示。

圖 2. 適用於偵錯建構類型的新來源集目錄。

您也可以使用相同的程序,分別為變種版本以及建構變數建立來源集目錄,例如 src/demo/src/demoDebug/。此外,您也可以建立指定特定建構變數的測試來源集,例如 src/androidTestDemoDebug/。詳情請參閱測試來源集

變更預設來源設定

如果您的來源未彙整成 Gradle 希望的預設來源集檔案結構,如上方建立來源集一節所述,您可以使用 sourceSets 區塊,變更 Gradle 希望為來源集各個元件收集檔案的位置。sourceSets 區塊必須位於 android 區塊中。您不需要重新調整來源檔案位置,只需要向 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 {
  ...
  // Encapsulates configurations for the main source set.
  sourceSets.getByName("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.
  sourceSets.getByName("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")
      ...
  }
}
...

請注意,來源目錄只能屬於一個來源集。例如,您無法對 testandroidTest 來源集共用相同的測試來源。這是因為 Android Studio 會為每個來源集分別建立 IntelliJ 模組,而且無法跨來源集支援重複內容根目錄。

使用來源集進行建構

您可以讓來源集目錄,納入您想要只封裝某些設定的程式碼和資源。舉例來說,如果您想要建構的「demoDebug」建構變數是「試用版」變種版本和「偵錯」建構類型的交叉產品,Gradle 會查看這些目錄,並設定以下優先順序:

  1. src/demoDebug/ (建構變化版本來源集)
  2. src/debug/ (建構類型來源集)
  3. src/demo/ (變種版本來源集)
  4. src/main/ (主要來源集)

為變種版本組合建立的來源集必須包含所有版本維度。舉例來說,建構變化版本來源集必須是 (建構類型 + 所有版本維度) 的組合。系統不支援合併涉及涵蓋多個 (但非全部) 版本維度的資料夾的程式碼和資源。

注意事項:如果您結合多種變種版本,則變種版本的優先順序取決於所屬的版本維度。使用 android.flavorDimensions 屬性列出版本維度時,來自您列出的第一個版本維度的變種版本優先順序高於第二個版本維度中的變種版本,依此類推。此外,您為各種變種版本組合建立的來源集優先順序也高於每個單獨變種版本的來源集。

當 Gradle 合併程式碼和資源時,上述順序將決定優先順序較高的來源集。demoDebug/ 來源集目錄可能包含該版本的專屬檔案,如果 demoDebug/ 也包含 debug/ 中定義的檔案,Gradle 就會使用demoDebug/ 來源集中的檔案。同樣地,針對建構類型與變種版本來源集中的檔案,Gradle 會為其指定高於 main/ 中相同檔案的優先順序。Gradle 套用下列建構規則時,會優先考量這個優先順序:

  • java/ 目錄中的所有原始碼會匯總在一起,以產生單一輸出內容。

    注意事項:在特定的建構變數中,如果 Gradle 遇到兩個或以上的來源集目錄定義同一個 Java 類別,會擲回建構錯誤。例如,建構偵錯應用程式時,您無法定義 src/debug/Utility.javasrc/main/Utility.java。可能的原因是,Gradle 會在建構程序中查看這兩個目錄,並擲回「類別重複」錯誤。如果您想要為不同的建構類型指定不同版本的 Utility.java,可以讓每個建構類型定義自己的檔案版本,而不是將其加入 main/ 來源集。

  • 資訊清單會合併為單一資訊清單。系統會按照上方清單中的順序排定優先順序。也就是說,版本類型的資訊清單設定會覆寫變種版本的資訊清單設定等。詳情請參閱資訊清單合併說明。
  • 同樣地,values/ 目錄中的檔案會合併在一起。如果兩個檔案共用相同名稱 (例如兩個 strings.xml 檔案),系統會按照上述清單的優先順序來排定優先順序。也就是說,由建構類型來源集中的檔案定義的值會覆寫變種版本中相同檔案定義的值,以此類推。
  • res/asset/ 目錄中的資源會封裝在一起。如果在兩個或以上的來源集中定義了相同名稱的資源,系統會按照上述清單中的順序排定優先順序。
  • 最後,在建構應用程式時,Gradle 會為隨附程式庫模組依附元件的資源和資訊清單,設定最低的優先順序。

宣告依附元件

您可以在 Implementation 關鍵字之前,加上建構變數或測試來源集的名稱做為前綴,藉此設定特定建構變數或測試來源集的依附元件,如以下範例所示:

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

詳情請參閱「新增建構依附元件」。

使用管理 variant-aware 依附元件

Android 外掛程式 3.0.0 及以上版本含有新的依附元件機制,會在使用資料庫時自動比對變體。這表示應用程式的 debug 變化版本會自動採用程式庫的 debug 變化版本,以此類推。也適用於變種版本,也就是說,應用程式的 freeDebug 變化版本會佔用程式庫的 freeDebug 變化版本。

為了讓外掛程式能正確比對變數,當無法直接進行比對時,您需要為執行個體提供比對備用選項。請考慮您的應用程式是否設定了名為「測試環境」的建構類型,而其中一個程式庫依附元件不是該類型。當外掛程式嘗試建立「測試環境」版應用程式時,將無法得知要使用的程式庫版本,此時,系統會顯示類似下方的錯誤訊息:

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

解決與變數比對相關的建構錯誤

這個外掛程式包含 DSL 元素,可協助您控制 Gradle 應如何解決應用程式與依附元件無法直接比對變數的情況。請參閱下表,判斷您應使用哪些 DSL 屬性來解決與變數感知依附元件比對相關的特定建構錯誤。

建構錯誤的原因 解析度

應用程式含有程式庫依附元件的建構類型

舉例來說,您的應用程式含有「測試環境」的建構類型,但依附元件中只有「偵錯」和「發布」建構類型。

請注意,如果程式庫依附元件包含應用程式沒有的建構類型,就不會發生問題。這是因為外掛程式絕對不會要求從依附元件建構類型的要求。

使用 matchingFallbacks 指定特定建構類型的替代相符項目,如下所示:

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

對於應用程式及其資料庫依附元件中的特定版本維度,您的應用程式含有該程式庫沒有的版本

舉例來說,您的應用程式及其程式庫依附元件都含有「階層」版本維度。不過,應用程式內的「階層」維度包含「免付費」和「付費」的版本,但依附元件只包含相同維度的「試用」和「付費」版本。

請注意,指定的版本維度會同時存在於應用程式及其程式庫依附元件中,因此如果程式庫包含應用程式不含的變種版本,則沒有任何問題。這是因為外掛程式絕對不會要求從依附元件接收到該版本。

使用 matchingFallbacks 指定應用程式「免付費」變種版本的替代相符項目,如下所示:

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 += "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」維度的版本,但您的應用程式僅含有「階層」維度的版本。因此,當您建構「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 參考資料中的 matchingFallbacksmissingDimensionsStrategy

設定簽署設定

除非您已明確為這個版本定義簽署設定,否則 Gradle 不會對發布版本的 APK 或 AAB 進行簽署。如果您還沒有簽署金鑰,請參閱如何使用 Android Studio 產生上傳金鑰和 KeyStore

使用 Gradle 建構設定,為您的發布建構類型手動設定簽署設定:

  1. 可建立 KeyStore。 KeyStore 是包含一組私密金鑰的二進位檔案。您必須將 KeyStore 存放在安全的地方。
  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")
            }
        }
    }

注意事項:最好不要在建構檔案中加入發布金鑰和 KeyStore 密碼,因為這並不安全。作為替代做法,您可以設定建構檔案,以便從環境變數取得這些密碼,或讓建構程序提示您輸入這些密碼。

如何從環境變數取得這些密碼:

Groovy

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

Kotlin

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

如要讓建構程序在您從指令行叫用建構時提示您輸入這些密碼,請新增以下程式碼:

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。

警告:請將 KeyStore 和私密金鑰妥善保存在安全的地方,並確保已經建立安全備份。如果您使用 Play 應用程式簽署功能,當上傳金鑰遺失時,可以透過 Play 管理中心要求重設。如果您發布的應用程式未使用 Play 應用程式簽署功能 (適用於 2021 年 8 月前建立的應用程式),而且已經遺失應用程式簽署金鑰,則您無法發布任何更新,因為您必須始終使用相同的金鑰對應用程序的所有版本進行簽署。

簽署 Wear OS 應用程式

發布 Wear OS 應用程式時,您必須簽署手錶 APK 和選用手機 APK (兩者皆可簽署)。如要進一步瞭解如何封裝及簽署 Wear OS 應用程式,請參閱封裝及發布 Wear 應用程式