Gradle for Android(五) 管理多模塊構(gòu)建

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中,moduleproject是有區(qū)別的。比如,一個(gè)模塊可以是一個(gè)Android app或者一個(gè)Google App Engine后臺。而一個(gè)工程是一個(gè)模塊的集合。本書為避免混淆,使用IDE中的概念來理解moduleproject。在瀏覽文檔時(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'

這保證了applibrary模塊包含在構(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è)后臺的客戶端模塊。

圖1 Android Studio創(chuàng)建App Engine模塊對話框

對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ù),用的最多的是appengineRunappengineUpdate

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ù)。

圖2 Android Studio分模塊展示任務(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ù)或者屬性來避免耦合。如果你需要這么做,你可以使用根模塊作為中介,這樣模塊就會變成和根模塊耦合,而不是相互耦合。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容