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,讓每個 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.2.0' apply false id 'com.android.library' version '7.2.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 利用並列專案執行功能,以至於無法加快多個模組的建構作業。
模組層級建構檔案
模組層級 build.gradle
檔案位於各個 project/module/
目錄中,可讓您對其所在的特定模組調整建構設定。調整相關建構設定會讓您得以提供自訂封裝選項,例如:額外的建構類型和變種版本,並可覆寫 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 { /** * The app's namespace. Used primarily to access app resources. */ namespace 'com.example.myapp' /** * 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 /** * 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 { // Uniquely identifies the package for publishing. 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 { /** * The app's namespace. Used primarily to access app resources. */ namespace = "com.example.myapp" /** * 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 { // Uniquely identifies the package for publishing. 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 發現您的設定有誤,例如原始碼使用了只能在
compileSdkVersion
以上的 API 層級中才會提供的 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 也會使用相同的優先順序。因此,各個建構變數都能在最終資訊清單中定義不同的元件或權限。如要進一步瞭解如何建立自訂來源集,請參閱「建立建構變數的來源集」。