Android Studio不僅可以為app或者library創(chuàng)建模塊,還可以為Android Wear,Android TV,Google App Engine等創(chuàng)建模塊。所有的這些模塊可以在一個(gè)工程中使用。你可能想要?jiǎng)?chuàng)建一個(gè)程序,它使用Google Cloud Endpoints作為后臺,并與Android Wear集成。這種情況下,你的工程中需要有三個(gè)模塊:app、后臺、Android Wear集成。了解多模塊工程的結(jié)構(gòu)和構(gòu)建,可以極大縮短你的開發(fā)周期。
Gradle和Gradle Android插件的文檔都使用了多工程構(gòu)建(multiproject builds)這一術(shù)語。在Android Studio中,
module和project是有區(qū)別的。比如,一個(gè)模塊可以是一個(gè)Android app或者一個(gè)Google App Engine后臺。而一個(gè)工程是一個(gè)模塊的集合。本書為避免混淆,使用IDE中的概念來理解module和project。在瀏覽文檔時(shí)要謹(jǐn)記。
本章講解多模塊構(gòu)建,并提供一些對實(shí)際工程很有用的示例。
- 多模塊構(gòu)建的剖析
- 為工程添加模塊
- 技巧和最佳實(shí)踐
多模塊構(gòu)建的剖析
通常一個(gè)多模塊工程會有一個(gè)根目錄,每一個(gè)模塊有一個(gè)子目錄。為了讓Gradle知道工程的結(jié)構(gòu),以及每個(gè)子目錄是什么模塊,你需要在根目錄添加一個(gè)settings.gradle文件。每個(gè)模塊可以提供它自己的build.gradle文件。我們在第二章已經(jīng)介紹了settings.gradle文件和build.gradle文件的作用,這里我們介紹如何在多模塊工程中使用它們。
下面是多模塊工程的結(jié)構(gòu):
project
├─── setting.gradle
├─── build.gradle
├─── app
│ └─── build.gradle
└─── library
└─── build.gradle
這是設(shè)置多模塊工程最簡單和直接的方式。settings.gradle文件聲明了工程的所有模塊:
include ':app', ':library'
這保證了app和library模塊包含在構(gòu)建配置中。你需要做的只是將模塊所在的目錄添加進(jìn)來。
你需要將以下代碼添加到app模塊的build.gradle文件中,以便app模塊將library模塊添加為依賴:
dependencies {
compile project(':library')
}
為給模塊添加依賴,你需要使用project()方法,參數(shù)為模塊路徑。
如果你想使用子目錄來組織模塊,Gradle也可以滿足需求。比如,你的目錄結(jié)構(gòu)可能是這個(gè)樣子的:
project
├─── setting.gradle
├─── build.gradle
├─── app
│ └─── build.gradle
└─── libraries
├─── library1
│ └─── build.gradle
└─── library2
└─── build.gradle
app模塊還在根目錄下,但工程有兩個(gè)庫。這些庫不在工程的根目錄下。你可以在settings.gradle文件中如下聲明各個(gè)模塊:
include ':app', ':libraries:library1', ':libraries:library2'
可以看到聲明子目錄中的模塊也是很容易的。路徑是相對于根目錄(settings.gradle文件所在的目錄)的。冒號用于替換路徑中的斜杠。
在將一個(gè)子目錄模塊添加為另一個(gè)模塊的依賴時(shí),你應(yīng)該從根目錄引用它。也就是說,如果上例中的app模塊依賴于library的話,app模塊的builde.gradle文件應(yīng)該這樣寫:
dependencies {
compile project(':libraries:library1')
}
Gradle在構(gòu)建工程依賴時(shí),總是相對于根目錄。
重溫構(gòu)建的生命周期
了解構(gòu)建過程模型是如何構(gòu)造的,可以更容易地理解多模塊項(xiàng)目是如何組成的。我們已經(jīng)在第一章提到過構(gòu)建的生命周期,所以你已經(jīng)有了基礎(chǔ),但是一些細(xì)節(jié)對于多模塊構(gòu)建來說也是很重要的。
在第一階段,即initialization期間,Gradle首先找到settings.gradle文件。如果這個(gè)文件不存在,Gradle認(rèn)為這是一個(gè)單模塊的構(gòu)建。如果你有多個(gè)模塊,你需要在settings文件中定義包含每個(gè)模塊的子目錄。如果這些子目錄有它們自己的build.gradle文件,Gradle將會自動處理,并將它們合并到構(gòu)建過程模型中。這解釋了為什么你需要在模塊中用相對路徑聲明依賴。Gradle會嘗試從根目錄中找出依賴項(xiàng)。
一旦你了解了構(gòu)建過程模型是如何組合在一起的,配置多模塊構(gòu)建的幾種策略將變得非常清晰。
- 你可以在根目錄的
build.gradle文件中配置所有模塊。這會使瀏覽工程的整個(gè)構(gòu)建配置變得很容易,但是結(jié)構(gòu)會混亂,尤其是在各個(gè)模塊需要不同的插件,每個(gè)插件擁有自己的DSL時(shí)。 - 另一種方式是為每個(gè)模塊單獨(dú)配置
build.gradle文件。這可以解耦各個(gè)模塊。它還使跟蹤構(gòu)建更改變得容易,因?yàn)槟悴恍枰滥膫€(gè)模塊應(yīng)用了哪個(gè)更改。 - 最后一個(gè)策略是混合使用。你可以在根目錄的構(gòu)建文件中定義所有模塊通用的屬性,在每個(gè)模塊的構(gòu)建文件中定義各自模塊的配置。Android Studio使用了這個(gè)方式。它會在根目錄創(chuàng)建一個(gè)
build.gradle文件,并為各個(gè)模塊創(chuàng)建自己的build.gradle文件。
模塊任務(wù)
一旦你的工程中有了多個(gè)模塊,你就需要在運(yùn)行任務(wù)前多思考一下。當(dāng)你在工程根目錄下通過命令行窗口運(yùn)行任務(wù)時(shí),Gradle會找出所有模塊中的同名任務(wù),并運(yùn)行它們。比如,你有一個(gè)app模塊和一個(gè)Android Wear模塊,運(yùn)行gradlew assembleDebug將會為app模塊和Android Wear模塊都構(gòu)建一個(gè)debug版本。如果你切換到模塊的目錄下,Gradle將只會運(yùn)行該模塊的任務(wù),即使你在工程根目錄中運(yùn)行Gradle Wrapper。比如,在Android Wear模塊的目錄運(yùn)行../gradlew assembleDebug,只會構(gòu)建Android Wear模塊。
切換目錄的方式來運(yùn)行模塊的任務(wù)是比較繁瑣的。另一個(gè)方式是可以使用模塊名稱來預(yù)處理任務(wù)名稱,使運(yùn)行任務(wù)只在那個(gè)模塊上運(yùn)行。比如只想構(gòu)建Android Wear模塊,可以使用gradlew :wear:assembleDebug命令。
為工程添加模塊
Android Studio有向?qū)б龑?dǎo)你簡單易懂的添加一個(gè)模塊。向?qū)б矔闃?gòu)建文件添加一些基本內(nèi)容。某些情況下,添加一個(gè)模塊會使Android Studio編輯app模塊的構(gòu)建文件。比如,在添加一個(gè)Android Wear模塊的時(shí)候,IDE會認(rèn)為你想在Android app中使用它,因此會在構(gòu)建文件中添加對Android Wear模塊的引用。
在接下來的部分,我們將會展示Android Studio中的工程可以添加的幾個(gè)不同的模塊,介紹它們的屬性,以及它們?nèi)绾斡绊憳?gòu)建過程。
添加一個(gè)Java庫
在你添加一個(gè)新的Java庫模塊的時(shí)候,build.gradle文件如下:
apply plugin: 'java'
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}
Java庫模塊使用了Java插件而不是我們經(jīng)常看到的Android插件。這表示很多Android特有的屬性和任務(wù)是無法使用的,而對于Java庫來說也并不需要它們。
構(gòu)建文件還建立了基本依賴管理,所以你可以在libs文件夾添加JAR包,而不需要任何特殊配置。你可以用第三章學(xué)到的知識添加更多的依賴。依賴配置不依賴于Android插件。
比如,為了給app模塊添加一個(gè)名為javalib的Java庫模塊你只需要在app模塊的構(gòu)建文件中添加一行代碼:
dependencies {
compile project(':javalib')
}
這樣Gradle就會在構(gòu)建時(shí)導(dǎo)入一個(gè)名為javalib的模塊。如果你在app模塊添加了這個(gè)依賴,那么在app模塊開始構(gòu)建之前,javalib模塊會先被構(gòu)建。
添加一個(gè)Android庫
我們在第三章簡單提及了Android庫,并稱之為庫工程。在文檔和各種教程中,都用到了這兩個(gè)名稱。在這一部分內(nèi)容中,我們使用Androidlibrary(Android庫)這個(gè)名稱,因?yàn)檫@是在Android Studio的New Module對話框中使用的名稱。
Android庫的build.gradle文件起始代碼如下:
apply plugin: 'com.android.library'
添加Android庫為依賴和添加Java庫是一樣的:
dependencies {
compile project(':androidlib')
}
一個(gè)Android庫不僅包含Java代碼,還包括所有的Android資源,比如manifest文件、字符串和布局等。添加Android庫為依賴后,你可以使用庫中所有的類和資源。
集成Android Wear
如果你想在Android Wear中加入深度集成的應(yīng)用程序,你需要添加Android Wear模塊。Android Wear模塊同樣使用了Android application插件,所以所有的構(gòu)建屬性和任務(wù)都可以使用。
build.gradle文件唯一和常規(guī)Android app模塊不同的部分是依賴配置:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.google.android.support:wearable:1.1.0'
compile 'com.google.android.gms:play-services-wearable:6.5.87'
}
每個(gè)Android Wear應(yīng)用都依賴一些Google提供的Wear特有的庫。為了在Android app中使用Android Wear app,你需要將它打包進(jìn)app中。你可以在Android app中添加一個(gè)依賴:
dependencies {
wearApp project(':wear')
}
wearApp配置確保Wear APK打包進(jìn)最終的Android app的APK中,并為你做了必要的配置。
使用Google App Engine
Google App Engine是一個(gè)云計(jì)算平臺,您可以使用它來托管web應(yīng)用程序,而無需設(shè)置自己的服務(wù)器。在一定程度上它是免費(fèi)的,這使它成為一個(gè)很好的實(shí)驗(yàn)環(huán)境。Google App Engine還提供一個(gè)稱謂Cloud Endpoints(云終端)的服務(wù),它可以用來創(chuàng)建基于REST的服務(wù)。使用Google App Engine和Cloud Endpoints可以很容易地為你的應(yīng)用構(gòu)建一個(gè)后臺。Gradle App Engine插件通過為你的app生成一個(gè)客戶端庫使其變得更加簡單,這意味著你不需要自己去寫任何API相關(guān)的代碼。這就使Google App Engine成為app后臺的一個(gè)選擇,所以結(jié)下來的部分我們會學(xué)習(xí)App Engine如何工作,以及如何使用Cloud Endpoints。
為了創(chuàng)建一個(gè)新的包含Cloud Endpoints的Google App Engine模塊,你需要從File|New Module...菜單打開New Module對話框,然后選擇Google Cloud Module。在模塊設(shè)置中,你可以修改種類來包含Cloud Endpoints。然后,選擇使用這個(gè)后臺的客戶端模塊。

對Google App Engine和Cloud Endpoints進(jìn)行透徹的講解超出了本書的范疇,我們只會講解Gradle對App Engine模塊和客戶端app模塊的集成。
分析構(gòu)建文件
這個(gè)模塊的構(gòu)建文件非常大,所以我們只關(guān)注幾個(gè)部分,首先是構(gòu)建腳本依賴:
buildscript {
dependencies {
classpath 'com.google.appengine:gradle-appengine-plugin:1.9.18'
}
}
App Engine插件需要定義在構(gòu)建腳本的類路徑中。我們在之前添加Android插件時(shí)已經(jīng)見到過。設(shè)置好之后,我們可以應(yīng)用App Engine插件和另外兩個(gè)插件:
apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'appengine'
Java插件主要用來為Cloud Endpoints生成Jar文件。WAR插件用來運(yùn)行和分發(fā)整個(gè)后臺。WAR插件生成一個(gè)WAR文件,Java web應(yīng)用在此分布。最后,App Engine插件添加一系列任務(wù)來構(gòu)建、運(yùn)行和配置整個(gè)后臺。
下一個(gè)重要的塊定義了App Engine模塊的依賴:
dependencies {
appengineSdk 'com.google.appengine:appengine-java-sdk:1.9.18'
compile 'com.google.appengine:appengine-endpoints:1.9.18'
compile 'com.google.appengine:appengine-endpoints-deps:1.9.18'
compile 'javax.servlet:servlet-api:2.5'
}
第一個(gè)依賴使用appengineSdk指明模塊需要用到的SDK。Cloue Endpoints需要endpoints依賴才能運(yùn)行。這些只在你使用Cloud Endpoints時(shí)才會添加。servlet依賴是每個(gè)Google App Engine模塊所必需的。
在appengine塊中設(shè)置App Engine特有的配置:
appengine {
downloadSdk = true
appcfg {
oauth2 = true
}
endpoints {
getClientLibsOnBuild = true
getDiscoveryDocsOnBuild = true
}
}
將downloadSdk屬性設(shè)置為true,可以很容易地運(yùn)行本地開發(fā)服務(wù)器,因?yàn)槿绻鸖DK不存在的話,它會自動下載SDK。如果你已經(jīng)在自己的設(shè)備上配置好了Google App Engine SDK,你可以將downloadSdk設(shè)置為false。
appcfg塊用來配置App Engine SDK。在一個(gè)典型的Google App Engine安裝中,你可以在命令行中使用appcfg來手動設(shè)置一些配置。使用appcfg塊來代替命令行工具,使設(shè)置更加便捷,每個(gè)構(gòu)建該模塊的人都有同樣的配置,從而不必去執(zhí)行一些額外的命令。
endpoints塊包含一些Cloud Endpoints特有的設(shè)置。
關(guān)于Google App Engine和Cloud Endpoints更詳細(xì)的說明超出了本書的范疇。如果你想學(xué)習(xí)更多,可以訪問https://cloud.google.com/appengine/docs
在app中使用后臺
當(dāng)你創(chuàng)建一個(gè)App Engine模塊時(shí),Android Studio會自動在Android app模塊的構(gòu)建文件中添加一個(gè)依賴。如下:
dependencies {
compile project(path: ':backend', configuration: 'android-endpoints')
}
我們已經(jīng)見過這種語法(在引用Java和Android庫時(shí)),使用project來定義依賴,并帶有兩個(gè)參數(shù)。path參數(shù)是默認(rèn)參數(shù),我們之前就用過,但是沒有指明它的名稱。一個(gè)Google App Engine模塊可以有不同種類的輸出。你可以使用configuration參數(shù)來指明你想要的輸出。我們需要App Engine模塊生成Cloud Endpoints,所以設(shè)置為android-endpoints。在內(nèi)部,這個(gè)配置會運(yùn)行_appengineEndpointsAndroidArtifact任務(wù)。這個(gè)任務(wù)生成一個(gè)JAR文件,包含你可以在Android app模塊中使用的類。這個(gè)JAR文件不僅包含Could Endpoints中使用的模型,還有API方法。像這樣的集成使得多模塊項(xiàng)目很好地工作,因?yàn)樗s短了開發(fā)時(shí)間。App Engine模塊中的Gradle任務(wù)也使運(yùn)行和部署后臺變得更加容易。
自定義任務(wù)
App Engine插件添加了很多任務(wù),用的最多的是appengineRun和appengineUpdate。
appengineRun任務(wù)用來啟動一個(gè)本地開發(fā)服務(wù)器,在上傳到Google App Engine之前,你可以用它來對整個(gè)后臺進(jìn)行本地測試。第一次啟動這個(gè)任務(wù)可能會多消耗一點(diǎn)時(shí)間,因?yàn)镚radle需要下載App Engine SDK(我們之前設(shè)置了downloadSdk = true)。你可以使用appengineStop來停止服務(wù)器運(yùn)行。
一旦你要將后臺部署到Google App Engine并在生產(chǎn)環(huán)境使用它,你可以使用appengineUpdate。這個(gè)任務(wù)處理部署的所有細(xì)節(jié)。如果你設(shè)置了oauth2 = true,這個(gè)任務(wù)會打開一個(gè)瀏覽器窗口,你可以登錄你的Google賬戶并拿到一個(gè)身份令牌。如果你不想每次部署時(shí)都需要登錄,你可以用Google賬戶登錄Android Studio并使用IDE部署后臺。Android Studio會運(yùn)行同樣的任務(wù),但是它會接管認(rèn)證。
技巧和最佳實(shí)踐
有一些技巧使處理多模塊工程變得簡單,在處理一些模塊時(shí),你需要牢記一些事情。了解這些可以使你省時(shí)又省心。
從Android Studio中運(yùn)行模塊任務(wù)
正如在第二章看到的,Android Studio可以直接運(yùn)行Gradle任務(wù)。當(dāng)你有多個(gè)模塊時(shí),Android Studio可以識別它們,并可以分模塊預(yù)覽可用的任務(wù)。

Gradle工具窗口可以很容易的運(yùn)行模塊特有的任務(wù)。窗口中并沒有為所有模塊同時(shí)運(yùn)行任務(wù)的選項(xiàng),所以如果你想這么做,還是需要使用命令行。
加快多模塊的構(gòu)建
在你構(gòu)建一個(gè)多模塊工程時(shí),Gradle會串行處理所有的模塊。在電腦有多核可用時(shí),我們可以利用并行處理加速構(gòu)建過程。Gradle已經(jīng)有這個(gè)特性,但是默認(rèn)沒有開啟。
如果你想開啟并行處理,需要在工程根目錄的gradle.properties文件中設(shè)置parallel屬性:
org.gradle.parallel=true
Gradle會基于CPU的核心數(shù)量選擇適當(dāng)?shù)木€程數(shù)。為了避免同時(shí)執(zhí)行一個(gè)模塊的兩個(gè)任務(wù)可能產(chǎn)生的問題,一個(gè)模塊會在一個(gè)線程中執(zhí)行。
并行構(gòu)建執(zhí)行是一個(gè)孵化特性。這意味著它在積極的發(fā)展,某個(gè)時(shí)候可能會改變。這個(gè)特性目前是Gradle的一部分,并被廣泛使用。所以,它應(yīng)該不會突然消失或者改變。
開啟并行構(gòu)建可能會省掉你的大筆時(shí)間。為了更有效率的執(zhí)行,需要確保模塊沒有耦合。
模塊耦合
如第二章所見,可以在頂級構(gòu)建文件中,使用allprojects為所有的模塊定義屬性。在多模塊工程中,你可以在任何模塊中使用allprojects為工程中的所有模塊應(yīng)用屬性。Gradle甚至使你可以在一個(gè)模塊中引用另一個(gè)模塊的屬性。這個(gè)強(qiáng)大的特性可以使維護(hù)多模塊構(gòu)建變得簡單,弊端就是會造成模塊耦合。
一旦兩個(gè)模塊需要訪問彼此的任務(wù)或者屬性,則被認(rèn)為是耦合的。這會造成幾個(gè)后果。比如,你放棄了可移植性。如果你決定從項(xiàng)目中提取一個(gè)庫,那么在復(fù)制所有項(xiàng)目范圍的設(shè)置之前,你將無法構(gòu)建。模塊耦合也會影響并行構(gòu)建。在任何模塊使用allprojects塊將使并行構(gòu)建無效。當(dāng)你在任何模塊添加項(xiàng)目范圍的屬性時(shí)要牢記這些。
你可以通過不直接訪問其他模塊的任務(wù)或者屬性來避免耦合。如果你需要這么做,你可以使用根模塊作為中介,這樣模塊就會變成和根模塊耦合,而不是相互耦合。