Android 建構系統會彙整應用程式資源和編譯原始碼,並封裝至 APK 或 Android App Bundles,供您測試、部署、簽名及發布。Android Studio 使用進階建構工具包 Gradle,來自動化管理建構程序,同時讓您能客製化建構設定。每項建構設定都能指定自己專用的程式碼和資源,同時也能重複使用應用程式所有版本共享的部分。適用於 Gradle 的 Android 外掛程式可與建構工具包一同運作,來提供專屬於 Android 應用程式建構與測試的處理程序及可調整的設定。
Gradle 和 Android 外掛程式的執行獨立於 Android Studio 之外。也就是說,您可以在 Android Studio 內部、利用裝置上的指令列,或在未安裝 Android Studio 的裝置上 (例如持續整合伺服器),建構 Android 應用程式。如果您並非使用 Android Studio,可以透過指令列來瞭解如何建構及執行應用程式。建構作業的輸出內容不會因為您透過指令列、遠端裝置,或 Android Studio 來建立專案而有所差異。
注意:由於 Gradle 和 Android 外掛程式是獨立於 Android Studio 之外執行,因此需要您另外更新建構工具。請參閱版本資訊,瞭解如何更新 Gradle 和 Android 外掛程式。
透過 Android 建構系統的靈活性,您無須修改應用程式的核心文件也能客製化建構設定。本節將帶您瞭解 Android 建構系統作業的方式,以及它能如何協助您客製化、自動化多個建構設定。如單純想進一步瞭解如何部署應用程式,請參考用 Android Studio 來建構與執行。想立即使用 Android Studio 建立自訂建構設定,歡迎參考「設定建構變數」。
建構程序
建構程序牽涉到許多工具和程序的運作,它們會將您的專案轉換為 Android 應用程式套件 (APK) 或 Android App Bundle (AAB)。因建構程序有很多的調整空間,若能瞭解實際運作情形將會有所幫助。
圖 1:典型 Android 應用程式模組的建構程序。
如圖 1 所示,典型 Android 應用程式模組的建構程序同以下次序:
- 編譯器會先將原始碼轉換為 DEX (Dalvik Executable) 檔案,其中包括在 Android 裝置上執行的位元碼;而將其他內容轉換為已編譯資源。
- 視所選建構目標而定,分裝器會將 DEX 檔案和已編譯資源合併成 APK 或 AAB。接著必須先在 APK 或 AAB 檔上簽名後,該應用程式才能在 Android 裝置上安裝或發布至應用程式商店,例如 Google Play。
- 分裝器使用偵錯 KeyStore 或發布 KeyStore 來簽署 APK 或 AAB:
- 如果您打算建構僅用於測試和剖析的偵錯版本,分裝器會透過偵錯 KeyStore 來簽署您的應用程式。Android Studio 會自動使用偵錯 KeyStore 來設定新專案。
- 如果您打算建構向外部發布的發布版本,分裝器必須使用經您設定過的發布 KeyStore 來簽署應用程式。如要建立發布 KeyStore,請參閱用 Android Studio 簽署應用程式之說明。
- 在輸出終端 APK 之前,分裝器會使用 zipalign 工具來最佳化您的應用程式,以減少運作時在裝置上動用的記憶體空間。
在建構程序結束時,應用程式中會有偵錯/發布的 APK/AAB,以便您日後部署、測試或釋出給其他人使用。
Android Build 詞彙表
Gradle 和 Android 外掛程式可協助您設定下列部分的建構作業:
- 建構類型
- 建構類型規定了 Gradle 在建構及封裝應用程式時運用的特定屬性,通常是為發展生命週期的不同階段所設定。舉例來說,偵錯版本建構類型會啟用偵錯選項,並使用偵錯金鑰簽署應用程式;而發布版本建構類型可能會經過壓縮、混淆,並以發布金鑰簽署應用程式來以便未來發布。您必須指定至少一個建構類型,才能建構應用程式。而按照預設,Android Studio 會建立偵錯和發布兩個版本類型。在開始調整封裝設定之前,請瞭解如何設置建構類型。
- 變種版本
- 提供不同的「變種版本」相當於發布不同版本給使用者,例如免費版和付費版。您可以運用不同的程式碼和資源來客製化變種版本,但同時也能反覆使用各版本間一致的部分。變種版本可有可無,但需要您手動建立。在開始建立應用程式的不同版本之前,請參考設定變種版本。
- 產品建構變數
- 「建構變數」是指建構類型和變種版本結合之後的產物,也是 Gradle 用於建構應用程式的設定。透過建構變數,您就能建構變種版本開發階段的偵錯版本,或者已簽署的預備發布版本。雖然您不會親自設定產品建構變數,但透過參與建構類型與變種版本的調整仍可以直接影響該設定。建立其他建構類型或不同變種版本也會產生額外的產品建構變數。想瞭解如何建立及管理產品建構變數,請參閱設定產品建構變數總覽。
- 資訊清單項目
- 您可以在設定產品建構變數時,指定資訊清單檔案中一部分屬性的數值。這些建構值會覆寫資訊清單檔案中原本的數值。如果您希望系統產生一些不同的變數,如應用程式名稱、可支援的最小 SDK 版本或最適 SDK 版本,前面提到的功能就能派上用場。當同時存在多個資訊清單,Gradle 會合併資訊清單設定。
- 依附元件
- 建構系統會經由您的本地檔案系統和遠端存放區來管理專案依附元件。這樣您就不需要手動搜尋、下載,或複製依附元件的二進制包到專案目錄中。詳情請參考新增建構依附元件。
- 簽署
- 建構系統可讓您在建構設置中指定簽署設定,也能在程序期間自動於應用程式上簽名。建構系統會用預設金鑰和已知憑證來簽署偵錯版本,以免在建構期間收到需要輸入密碼的提示。除非您明確指定此建構作業的簽署方式,否則建構系統不會主動簽署發布版本。如果您還沒有發布金鑰,可以按照簽署應用程式的說明來產生。
- 程式碼和資源縮減
- 利用建構系統,您可以為每個產品建構變數指定不同的 ProGuard 規則文件。建構應用程式時,建構系統會採適當的規則,並使用系統內建的壓縮工具 (例如 R8),來縮減程式碼和資源。
- 支援多個 APK
- 透過建構系統可以自動建立不同的 APK,其中每一個都只包含特定的螢幕解析度或應用二進位介面 (ABI) 所需的程式碼和資源。詳情請參考建構多個 APK。請留意,在此我們仍會建議發布單一 AAB 格式。因為這項產品除螢幕解析度和 ABI 外,還能按照語言分割內容,同時降低將多個構件上傳至 Google Play 的複雜度。
建構設定檔
建立自訂建構設定時,您需要變更一至多個建構設定檔或 build.gradle
檔案。這些純文字檔用領域特定語言 (DSL) 來描述,並利用 Groovy 來操控建構邏輯。上述提及的 Groovy 是 Java 虛擬機器 (JVM) 的動態語言。不一定需要熟悉 Groovy 才能開始設定自己的版本,因為用於 Gradle 的 Android 外掛程式已包含大多數您需要的 DSL 元素。如要進一步瞭解 Android 外掛程式 DSL,請參閱DSL 參考文件。
開始一新專案時,Android Studio 會自動為您建立一些檔案 (如圖 2 所示),並根據預設填入一些資訊。
圖 2:Android 應用程式模組的預設專案結構。
有些 Gradle 建構設定檔是 Android 應用程式標準專案結構的一部分。因此在開始設定版本之前,請務必先瞭解這些檔案的適用範圍與目的,以及其所規定的 DSL 基本元素。
Gradle 設定檔
位於專案根目錄中的 settings.gradle
檔案,負責規定專案層級的存放區設定,並在建構應用程式時引導 Gradle 納入需要的模組。在大部分的專案中,檔案預設情況如下:
Groovy
pluginManagement { /** * The pluginManagement {repositories {...}} block configures the * repositories Gradle uses to search or download the Gradle plugins and * their transitive dependencies. Gradle pre-configures support for remote * repositories such as JCenter, Maven Central, and Ivy. You can also use * local repositories or define your own remote repositories. The code below * defines the Gradle Plugin Portal, Google's Maven repository, * and the Maven Central Repository as the repositories Gradle should use to look for its dependencies. */ repositories { gradlePluginPortal() google() mavenCentral() } } dependencyResolutionManagement { /** * The dependencyResolutionManagement { repositories {...}} * block is where you configure the repositories and dependencies used by * all modules in your project, such as libraries that you are using to * create your application. However, you should configure module-specific * dependencies in each module-level build.gradle file. For new projects, * Android Studio includes Google's Maven repository * and the Maven Central Repository by * default, but it does not configure any dependencies (unless you select a * template that requires some). */ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() } } rootProject.name = "My Application" include ‘:app’
Kotlin
pluginManagement { /** * The pluginManagement {repositories {...}} block configures the * repositories Gradle uses to search or download the Gradle plugins and * their transitive dependencies. Gradle pre-configures support for remote * repositories such as JCenter, Maven Central, and Ivy. You can also use * local repositories or define your own remote repositories. The code below * defines the Gradle Plugin Portal, Google's Maven repository, * and the Maven Central Repository as the repositories Gradle should use to look for its dependencies. */ repositories { gradlePluginPortal() google() mavenCentral() } } dependencyResolutionManagement { /** * The dependencyResolutionManagement { repositories {...}} * block is where you configure the repositories and dependencies used by * all modules in your project, such as libraries that you are using to * create your application. However, you should configure module-specific * dependencies in each module-level build.gradle file. For new projects, * Android Studio includes Google's Maven repository and the * Maven Central Repository by * default, but it does not configure any dependencies (unless you select a * template that requires some). */ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() } } rootProject.name = "My Application" include(":app")
多模組專案須指明應傳送至最終版本的所有模組。
頂層建構檔案
位於專案根目錄中的頂層 build.gradle
檔案會明確規定適用於專案中所有模組的依附元件。根據預設,頂層版本檔案使用 plugins
資料塊來規定專案內所有模組共用的 Gradle 依附元件。此外,頂層建構檔案也包含負責清除建構目錄的程式碼。以下程式碼範例描述的是,新專案建立之後,您可以在頂層 build.gradle
檔案中找到的預設設定和 DSL 元素。
Groovy
plugins { /** * You should use `apply false` in the top-level build.gradle file * to add a Gradle plugin as a build dependency, but not apply it to the * current (root) project. You should not use `apply false` in sub-projects. * For more information, see * Applying external plugins with same version to subprojects. */ id 'com.android.application' version '7.0.0' apply false id 'com.android.library' version '7.0.0' apply false id 'org.jetbrains.kotlin.android' version '1.6.10' apply false } task clean(type: Delete) { delete rootProject.buildDir }
Kotlin
plugins { /** * You should use `apply false` in the top-level build.gradle file * to add a Gradle plugin as a build dependency, but not apply it to the * current (root) project. You should not use `apply false` in sub-projects. * For more information, see * Applying external plugins with same version to subprojects. */ id("com.android.application") version "7.1.0-beta02" apply false id("com.android.library") version "7.1.0-beta02" apply false id("org.jetbrains.kotlin.android") version "1.5.30" apply false } tasks.register("clean", Delete::class) { delete(rootProject.buildDir) }
設定專案層級屬性
針對包含多個模組的 Android 專案,您可以在專案層級規範特定屬性,並與其他所有模組共用。只要在頂層 build.gradle
檔案的 ext
資料塊中加入額外屬性即可。
Groovy
// This block encapsulates custom properties and makes them available to all // modules in the project. The following are only a few examples of the types // of properties you can define. ext { sdkVersion = 28 // You can also create properties to specify versions for dependencies. // Having consistent versions between modules can avoid conflicts with behavior. supportLibVersion = "28.0.0" ... } ...
Kotlin
// This block encapsulates custom properties and makes them available to all // modules in the project. The following are only a few examples of the types // of properties you can define. ext { extra["compileSdkVersion"] = 28 // You can also create properties to specify versions for dependencies. // Having consistent versions between modules can avoid conflicts with behavior. extra["supportLibVersion"] = "28.0.0" ... } ...
想從相同專案中的模組存取這些屬性,請使用下列語法。這些語法可在模組的 build.gradle
檔案中找到 (想進一步瞭解這個檔案的內容,歡迎參閱下一節)。
Groovy
android { // Use the following syntax to access properties you defined at the project level: // rootProject.ext.property_name compileSdkVersion rootProject.ext.compileSdkVersion ... } ... dependencies { implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}" ... }
Kotlin
android { // Use the following syntax to access properties you defined at the project level: // rootProject.extra["property_name"] compileSdk = rootProject.extra["sdkVersion"] // Alternatively, you can access properties using a type safe delegate: val sdkVersion: Int by rootProject.extra ... compileSdk = sdkVersion } ... dependencies { implementation("com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}") ... }
注意:為防止共用這些專案屬性的模組發生耦合,即便 Gradle 允許,我們仍建議盡量避免在模組層級規範專案層級的屬性。模組耦合將讓一模組較難以獨立專案的方式匯出,並將明顯阻礙 Gradle 利用並列專案執行功能,以至於無法加快多個模組的建構作業。
模組層級建構檔案
位於每個 project/module/
目錄中的模組層級 build.gradle
檔案,可讓您調整特定模組的建構設定。調整這些建構設定會讓您得以提供自訂封裝選項,例如額外的建構類型和變種版本,並能覆寫 main/
應用程式資訊清單或頂層 build.gradle
檔案中的設定。
以下的 Android 應用程式模組 build.gradle
範例,大致列舉了一些您需要認識的 DSL 基本元素和設定,
Groovy
/** * The first line in the build configuration applies the Android plugin for * Gradle to this build and makes the android block available to specify * Android-specific build options. */ plugins { id 'com.android.application' } /** * The android block is where you configure all your Android-specific * build options. */ android { /** * compileSdkVersion specifies the Android API level Gradle should use to * compile your app. This means your app can use the API features included in * this API level and lower. */ compileSdkVersion 28 /** * buildToolsVersion specifies the version of the SDK build tools, command-line * utilities, and compiler that Gradle should use to build your app. You need to * download the build tools using the SDK Manager. * * This property is optional because the plugin uses a recommended version of * the build tools by default. */ buildToolsVersion "30.0.2" /** * The defaultConfig block encapsulates default settings and entries for all * build variants, and can override some attributes in main/AndroidManifest.xml * dynamically from the build system. You can configure product flavors to override * these values for different versions of your app. */ defaultConfig { /** * applicationId uniquely identifies the package for publishing. * However, your source code should still reference the package name * defined by the namespace property (for simplicity, keep the * applicationId and namespace the same). */ applicationId 'com.example.myapp' // Defines the minimum API level required to run the app. minSdkVersion 15 // Specifies the API level used to test the app. targetSdkVersion 28 // Defines the version number of your app. versionCode 1 // Defines a user-friendly version name for your app. versionName "1.0" } /** * The buildTypes block is where you can configure multiple build types. * By default, the build system defines two build types: debug and release. The * debug build type is not explicitly shown in the default build configuration, * but it includes debugging tools and is signed with the debug key. The release * build type applies Proguard settings and is not signed by default. */ buildTypes { /** * By default, Android Studio configures the release build type to enable code * shrinking, using minifyEnabled, and specifies the default Proguard rules file. */ release { minifyEnabled true // Enables code shrinking for the release build type. proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } /** * The productFlavors block is where you can configure multiple product flavors. * This allows you to create different versions of your app that can * override the defaultConfig block with their own settings. Product flavors * are optional, and the build system does not create them by default. * * This example creates a free and paid product flavor. Each product flavor * then specifies its own application ID, so that they can exist on the Google * Play Store, or an Android device, simultaneously. * * If you declare product flavors, you must also declare flavor dimensions * and assign each flavor to a flavor dimension. */ flavorDimensions "tier" productFlavors { free { dimension "tier" applicationId 'com.example.myapp.free' } paid { dimension "tier" applicationId 'com.example.myapp.paid' } } } /** * The dependencies block in the module-level build configuration file * specifies dependencies required to build only the module itself. * To learn more, go to Add build dependencies. */ dependencies { implementation project(":lib") implementation 'com.android.support:appcompat-v7:28.0.0' implementation fileTree(dir: 'libs', include: ['*.jar']) }
Kotlin
/** * The first section in the build configuration applies the Android plugin for * Gradle to this build and makes the android block available to specify * Android-specific build options. */ plugins { id("com.android.application") } /** * The android block is where you configure all your Android-specific * build options. */ android { /** * compileSdkVersion specifies the Android API level Gradle should use to * compile your app. This means your app can use the API features included in * this API level and lower. */ compileSdk = 28 /** * buildToolsVersion specifies the version of the SDK build tools, command-line * utilities, and compiler that Gradle should use to build your app. You need to * download the build tools using the SDK Manager. * * This property is optional because the plugin uses a recommended version of * the build tools by default. */ buildToolsVersion = "30.0.2" /** * The defaultConfig block encapsulates default settings and entries for all * build variants, and can override some attributes in main/AndroidManifest.xml * dynamically from the build system. You can configure product flavors to override * these values for different versions of your app. */ defaultConfig { /** * applicationId uniquely identifies the package for publishing. * However, your source code should still reference the package name * defined by the namespace property (for simplicity, keep the * applicationId and namespace the same). */ applicationId = "com.example.myapp" // Defines the minimum API level required to run the app. minSdk = 15 // Specifies the API level used to test the app. targetSdk = 28 // Defines the version number of your app. versionCode = 1 // Defines a user-friendly version name for your app. versionName = "1.0" } /** * The buildTypes block is where you can configure multiple build types. * By default, the build system defines two build types: debug and release. The * debug build type is not explicitly shown in the default build configuration, * but it includes debugging tools and is signed with the debug key. The release * build type applies Proguard settings and is not signed by default. */ buildTypes { /** * By default, Android Studio configures the release build type to enable code * shrinking, using minifyEnabled, and specifies the default Proguard rules file. */ getByName("release") { isMinifyEnabled = true // Enables code shrinking for the release build type. proguardFiles( getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" ) } } /** * The productFlavors block is where you can configure multiple product flavors. * This allows you to create different versions of your app that can * override the defaultConfig block with their own settings. Product flavors * are optional, and the build system does not create them by default. * * This example creates a free and paid product flavor. Each product flavor * then specifies its own application ID, so that they can exist on the Google * Play Store, or an Android device, simultaneously. * * If you declare product flavors, you must also declare flavor dimensions * and assign each flavor to a flavor dimension. */ flavorDimensions = "tier" productFlavors { create("free") { dimension = "tier" applicationId = "com.example.myapp.free" } create("paid") { dimension = "tier" applicationId = "com.example.myapp.paid" } } } /** * The dependencies block in the module-level build configuration file * specifies dependencies required to build only the module itself. * To learn more, go to Add build dependencies. */ dependencies { implementation(project(":lib")) implementation("com.android.support:appcompat-v7:28.0.0") implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar")))) }
Gradle 屬性檔案
Gradle 還包含兩個位於專案根目錄中的屬性檔案,可用來指定 Gradle 建構工具包本身的設定:
-
gradle.properties
- 在這裡您可以調整專案層級的 Gradle 設定,例如 Gradle Daemon 的最大堆積量。詳情請參考建構環境。
-
local.properties
- 設定建構系統的本地環境屬性,包括:
ndk.dir
- 到 NDK 的路徑。這個屬性已被淘汰。下載任何的 NDK 版本都會安裝在 Android SDK 目錄的ndk
目錄中。sdk.dir
- 到 SDK 的路徑。cmake.dir
- 到 CMake 的路徑。ndk.symlinkdir
- 在 Android Studio 3.5 或以上版本中建立到 NDK 的符號連結,其路徑可能會比已安裝的 NDK 路徑還要短。
將 NDK 重新映射到較短的路徑 (僅限 Windows)
有關 Windows 長路徑最常見的狀況是,安裝在 NDK 資料夾中的工具 (例如 ld.exe
) 常會儲存在過深的位置,但這些工具本身並不支援那麼長的路徑。
在 local.properties
中,您可以將屬性 ndk.symlinkdir
設定為要求 Gradle 外掛程式建立與 NDK 的符號連結。該符號連結的路徑可能會比現有 NDK 資料夾的路徑還短。舉例來說,ndk.symlinkdir = C:\
能夠生成右側的符號連結:C:\ndk\19.0.5232133
將專案與 Gradle 檔案同步
變更專案中的建構設定檔時,Android Studio 會要求您同步專案檔案,以便套用您的建構設定變更;並請您試跑一些作業,確認您的設定不會發生建構錯誤。
如要同步專案檔案,請在進行變更時按一下通知列中的「Sync Now」,如圖 3 所示。或在主選單列按一下「Sync Project」 。當 Android Studio 發現您的設定有誤,例如原始碼採用的 API 功能只能在比您的
compileSdkVersion
高一個 API 層級才能使用,這時在「Messages」視窗上便會描述該狀況。
圖 3:將專案與 Android Studio 中的建構設定檔同步。
來源集
Android Studio 會按照邏輯將所有模組的原始碼和資源分入不同的來源集。模組的 main/
來源集包含其所有建構變數所使用的程式碼和資源。外加的來源集目錄是選擇性的。因此當您設定新的建構變數時,Android Studio 不會自動建立這些目錄。然而建立來源集與 main/
概念類似,它可以協助您管理一些僅 Gradle 在建立特定應用程式版本時會動用的檔案和資源:
-
src/main/
- 這個來源集包含所有建構變數共用的程式碼和資源。
-
src/buildType/
- 建立這個來源集,以便納入特定建構類型的程式碼和資源。
-
src/productFlavor/
- 建立這個來源集,以便納入特定變種版本的程式碼和資源。
注意:如果您將產品建構設定為組合多個變種版本,則可以在不同版本維度間,為每個變種版本組合建立來源集目錄:
src/productFlavor1ProductFlavor2/
-
src/productFlavorBuildType/
- 建立這個來源集,以便納入特定建構變數的程式碼和資源。
舉例來說,如要產生應用程式的「fullDebug」版本,建構系統將會合併下列來源集的程式碼、設定和資源:
-
src/fullDebug/
(建構變數來源集) -
src/debug/
(建構類型來源集) -
src/full/
(變種版本來源集) -
src/main/
(主要來源集)
注意:在 Android Studio 中建立新檔案或目錄時,依序點一下「File」>「New」選單項目,即可鎖定建立給特定的來源集。您可以調整建構設定來選擇需要的來源集。而如果所需的目錄尚未建立,Android Studio 會自動建立完成。
當不同的來源集包含一個檔案的不同版本,Gradle 會以下列順序決定使用的檔案 (左側來源集會覆蓋右側來源集的檔案和設定):
建構變數 > 建構類型 > 變種版本 > 主要來源集 > 程式庫依附元件
這樣 Gradle 就能按照您建構的變數去選擇要使用的檔案,同時能夠重複使用各版本一致的作業、應用程式邏輯和資源。而合併多個資訊清單時,Gradle 也會使用相同的優先順序,如此每一個建構變數都將規範最終資訊清單中的不同元件或權限。想進一步瞭解如何建立自訂來源集,歡迎參考為建構變數建立來源集。