Gradle for Android-管理多模塊buld

AS不僅允許爲app或library創建module,也會創建Android Wear、Android TV、Google App Engine等。所有的這些模塊都可應用到一個單獨項目中。例如,你可能想要創建一個集成了Android Wear而且後臺使用Google Cloud Endpoints的app。這種情況下,要創建一個有三個module的項目:一個用於app,一個用於後臺,還有一個用於Android Wear集成。瞭解多模塊項目如何被組織和構建可以極大的縮減開發週期。

本章中,我們將會涉及多模塊構建的理論以及展示一些在實際項目中非常有用的例子:

  • 多模塊構建的剖析
  • 給項目添加模塊
  • 技巧和最佳實踐

多模塊build剖析

一般來說,一個多模塊項目靠根目錄的子目錄中包含的所有的模塊工作。爲了告訴gradle項目是如何被構成以及哪些目錄下包含模塊,需要在項目的根目錄下提供一個settings.gradle文件。每個模塊也會提供自己的build.gradle文件。我們已經在第二章學習了settings.gradle和build.gradle是如何工作的,所以這裏我們只會聚焦於如何在多模塊項目中使用它們。

一個多模塊項目看起來如下:

project
├─── setting.gradle
├─── build.gradle
├─── app
│       └─── build.gradle
└─── library
        └─── build.gradle

這是建立多模塊項目最簡單也是最直接的方式。settings.gradle文件聲明瞭項目中所有的module,如是下觀:

include ':app', ':library'

確保了app和library模塊都被在build配置中添加。你所需要做的只是添加模塊目錄的名稱。

爲了添加library模塊作爲app模塊的依賴,需要把如下代碼添加到app模塊的build.gradle文件中:

dependencies {
    compile project(':library')
}

爲了給模塊添加依賴,需要使用project()方法,並使用模塊路徑作爲參數。

如果想要使用子目錄組織模塊,gradle可被配置滿足你的需求。例如,你可能有如下目錄結構:

project
├─── setting.gradle
├─── build.gradle
├─── app
│ └─── build.gradle
└─── libraries
        ├─── library1
        │      └─── build.gradle
        └─── library2
                └─── build.gradle

app模塊依舊如之前一般位於根目錄下,但是項目現在有兩個不同的庫。這些庫模塊並沒有位於根目錄下,而是在一個特殊的庫目錄下。對於這種目錄結構,可在settings.gradle中如是聲明app和library模塊:

include ':app', ':libraries:library1', ':libraries:library2'

可以看到到在子目錄中聲明模塊是多麼容易啊!所有路徑都是關聯到根目錄(settings.gradle文件所在即是)的。路徑中冒號代替斜槓。

當在子目錄中添加一個模塊作爲其他模塊的依賴,應該總是從根目錄中引用。也就是說在之前的例子中如果app模塊依賴library1,app模塊的build.gradle文件應該如是下觀:

dependencies {
    compile project(':libraries:library1')
}

如果在子目錄中聲明依賴,所有的路徑仍然要被關聯到根目錄。原因在於gradle從項目根路徑下開始構造項目的依賴模型。

build生命週期再觀

瞭解build過程模型是如何被構造的使得很容易理解多模塊項目是如何組成的。我們已經在第一章討論過了build的生命週期,所以你已經瞭解了基礎,但是有些細節對於多模塊構建尤其重要。

在第一階段—初始化階段,gradle尋找settings.gradle文件。如果文件不存在,gradle設想你只有一個單模塊要構建。但如果有多個模塊,settings.gradle就是定義包含單個模塊的子目錄的地方。如果這些模塊包含自己的build.gradle文件,gradle將會處理並把它們合併到build過程模型中。這也解釋了爲什麼應該總是在模塊中使用與根目錄關聯的路徑聲明依賴。gradle將會一直嘗試從根目錄中解決依賴。

一旦你理解了構建過程模型是如何被組合的,有好幾種配置多模塊項目構建的策略都會很清晰。你應該在根目錄下的build.gradle文件中爲所有的模塊配置設置。這使得獲取一個項目中整個的構建配置概覽很容易,但也有可能會變得很凌亂,尤其當模塊需要不同插件,而且每個插件都有自己的DSL時。另一種方式就是每個模塊都有自己的build.gradle文件。這種策略保證了模塊之間不會緊密耦合。也使得更容易跟蹤構建改變,因爲不需要計算改變要應用到哪些模塊中。

最後一種策略就是混合算法。在項目的根目錄下有個build.gradle文件,主要定義一些針對所有模塊的公共屬性,和一個每個模塊用來配置僅應用到特定模塊的設置的build文件。AS採用的就是這種方式。在根目錄下創建了一個build.gradle文件和一個針對模塊的build.gradle文件。(此處我覺得應該是根目錄下有個build文件,每個模塊下都有自己的build文件

模塊task

只要在項目中有多個模塊,運行task之前就要再三考慮。當你在命令行接口中從項目的根目錄運行一個task,gradle就會判斷哪些模塊具有這個名字的task,然後爲所有模塊運行該task。例如,如果有一個移動app模塊和一個Android Wear模塊,運行gradlew assembleDebug將會分別構建移動app模塊和Android Wear模塊的debug版本。當你把目錄改成爲其中的一個模塊的路徑時,然而,gradle將只會爲那個特殊的模塊運行task,甚至當你在項目的根目錄使用gradle wrapper時。

切換目錄運行特定模塊的task實在令人生厭。幸運的是,還有其他的方式。可以使用具有模塊名稱的task名稱運行鍼對特地模塊的task。例如,爲了只構建Android Wear模塊,可以使用如下命令:

gradlew:wear:assembleDebug

給項目增加模塊

添加一個新的模塊就像在AS中經歷一個嚮導程序一樣簡單。嚮導程序也建立了基礎的build文件。一些情況下,添加一個新模塊甚至可能會導致AS編輯你的app模塊的build文件。例如,當添加一個Android Wear模塊時,IDE設想你想要爲你的Android app使用它,並且會在build文件中添加一行代碼引用Android Wear模塊。

AS中New Module對話框如是下觀:

這裏寫圖片描述

在下面的段落中,我們將會展示可以被添加到Android項目中的不同的模塊,解釋它們的自定義屬性和說明它們是如何改變構建進程的。

添加Java庫

添加一個新的Java library模塊時,AS生成的build.gradle文件如是下觀:

apply plugin: 'java'
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
}

Java library模塊使用Java插件代替我們習慣看到的Android插件。這意味着大量的Android特有的屬性和task都無法使用,但是對於Java library這些也都用不到。

build文件也會建立基礎的依賴管理,所以可以添加JAR文件到libs目錄下而無需任何特殊的配置。可以使用在第三章學習到的添加更多的依賴。依賴配置並不依賴於Android插件。

爲了添加一個名爲javalib的Java library模塊作爲你的app模塊的依賴,簡單的在app模塊的build配置文件中添加如下代碼:

dependencies {
    compile project(':javalib')
}

者告訴gradle在build中導入名爲javalib的模塊。如果在app模塊中添加這個依賴,javalib模塊將一直先於app模塊被構建。

添加Android庫

我們在第三章簡單地提到過Android library,在第三章我們稱之爲library項目。兩個名字在本文檔和其他各種材料中都被使用。在本節,我們將會使用Android library,因爲它是在AS的New Module對話框中使用的名稱。

對於一個Android library,默認的build.gradle文件第一行代碼如是下觀:

apply plugin: 'com.android.library'

一個Android library不僅包含了library的java代碼,也包含了所有的Android資源,例如manifest文件、strings和layouts。在app中應用一個Android library後,可以在app中使用library中所有的類和資源。

集成Android可穿戴

如果想要添加app的深度集成到Android Wear中,需要添加一個額外的Android Wear模塊。有意思的是Android Wear模塊也使用Android應用插件。這意味着所有的屬性和task都是可用的。

與常規的Android app模塊中build.gradle文件的唯一不同之處就在於依賴配置:

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'
}

每個Android Wear app依賴一些Google提供的Wear特有的庫。爲了在Android app中使用Android Wear app,需要把它和app一起打包。可通過在app中添加依賴實現:

dependencies {
    wearApp project(':wear')
}

wearApp配置確保了Wear模塊的APK被添加到了Android app的最終APK中而且已經完成了必備的設置。

使用Google App Engine

Google App Engine是個雲平臺。如果沒有建立自己的服務器的話,可以在上面運行web程序。可免費使用一定級別的,因此是個不錯的測試環境。Google App Engine也提供一個叫做Cloud Endpoint的服務,它主要用來創建RESTful服務。使用Google App Engine的Cloud EndPoint使得爲你的app建立後臺很容易。App Engine Gradle插件通過爲app生成一個客戶端庫使之更加容易,意味着你不需要寫任何與APII相關的代碼。這使得對於app後臺來說,Google App Engine是個不錯的選擇。所以在接下來的章節中我們將會了解App Engine Gradle插件是如何工作的以及我們可以如何充分利用Cloud Endpoints。

爲了創建一個帶有Cloud Endpoints的新的Google App Engine模塊,從File|new Module中打開New Module對話框…選擇Google Cloud Module。當建立module時,可以改變Cloud Endpoints的添加類型。然後,然後選擇將會使用這個後臺的客戶端模塊。

這裏寫圖片描述

關於Google App Engine和Cloud Endpoints的完整解釋超出了本書的範圍;我們將只會瞭解App Engine模塊和客戶端app模塊的Gradle集成。

分析build文件

這個模塊的build.gradle文件比較大,所有我只會了解最值得關注的地方,從新的build腳本依賴開始:

buildscript {
    dependencies {
        classpath 'com.google.appengine:gradle-appengineplugin:1.9.18'
    }
}

App Engine插件需在build腳本的classpath中定義。在之前添加Android插件時我們已看到過。當各得其所時,可以把App Engine與其他兩種插件一起使用

apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'appengine'

Java插件主要用於爲Cloud Endpoints生成JAR文件。WAR插件是運行和分發整個後端必備的。WAR插件生成一個WAR文件,這就是Java Web程序如何是分佈式的。最後,App Engine插件添加了一些構建、運行和部署整個後端的task。

下一個重要的塊定義了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'
}

第一個依賴使用appengineSdk指明應該在這個模塊中使用哪個sdk。endpoints依賴對於Cloud Endpoints工作是必須的。如果選擇在模塊中使用Cloud EndPoints,這些就是全部需要添加的。servlet依賴是任何Google App Engine模塊都要求具有的。

在appengine塊中配置任何App Engine特有的設置:

appengine {
    downloadSdk = true
    appcfg {
        oauth2 = true
    }
    endpoints {
        getClientLibsOnBuild = true
        getDiscoveryDocsOnBuild = true
    }
}

設置downloadSdk屬性值爲true使得運行本地開發服務更容易,因爲如果沒有的話會自動下載SDK。如果已經在設備上設置Google App Engine SDK,可以設置downloadSdk爲false。

appcfg塊是用來配置App Engine SDK的。在一個典型的Google App Engine安裝中,可以使用appcfg命令行工具手動配置一些設置。使用appcfg塊,代替命令行工具,可使配置更加輕便。任何曾經構建過該模塊的人將會有相同的配置而無需執行任何額外的命令。

endpoints塊包含一些Cloud Endpoints特定的設置。

關於Google App Engine和Cloud Endpoints配置的詳細說明超出了本書範圍,如果想了解更多,可以查看這篇文檔:https://cloud.google.com/appengine/docs

在app中使用後臺

當你創建App Engine模塊時,AS自動給Android app模塊的build文件添加一個依賴,如是下觀:

dependencies {
    compile project(path: ':backend', configuration: 'androidendpoints')
}

語法類似於之前的(當音樂Java 和Android庫時)。使用project定義依賴,但是有兩個參數。path參數是默認參數。我們之前使用過,但是沒有指定名稱。一個Google App Engine可有不同類型的輸出。可以使用configration指定你想要的輸出。我們需要App Engine模塊生成Cloud Eendpoints,所以使用android-endpoints配置。在內部,該配置運行_appengineEndpointsAndroidArtifact task。這個task生成了一個包含可以在app模塊中使用的類的JAR文件。這個JAR文件不僅僅包含了在Cloud Endpoints使用的模式,也包含了API方法。這種集成就是使得多模塊項目很好工作的原因,因爲它加速了縮減時間。App Engine模塊中的gradle task也使之更易運行和部署後臺。

自定義task

App Engine插件添加了很多task,但是你將使用最多的就是appengineRun和appengineUpdate。

appengineRun task用來啓動一個本地開發服務器,以便可以在上傳到Google App Engine之前在本地測試整個後臺。第一次運行這個task,因爲gradle需要下載App Engine SDK,build可能會花費一些時間。我們之前使用downloadSdk設置該行爲。爲了停止服務器,可以使用appengineStop。

你一旦準備部署後臺到Google App Engine並在產品中使用,可以使用appengineUpdate。這個task處理所有的部署細節。如果在appengine配置塊中設置了oauth2 = true,這個task將會打開一個瀏覽器窗口。所以你可以登陸google賬號並獲取認證令牌。如果不想每次需要部署的時候都這樣,可以使用Google賬號登陸到AS中並使用IDE部署後臺。AS運行相同的gradle task,但是將會爲你負責認證。

技巧和最佳實踐

有幾種方式可以使得處理多模塊項目更加容易,處理好幾個模塊時也需要注意一些事情。意識到這些可以節約你的時間和減少挫折。

從AS中運行模塊task

正如我們在第二章看到的,可以直接從從AS中運行gradle task。當你有多個模塊時,AS會識別到它們並將展示出來:

這裏寫圖片描述

gradle工具窗口使得運行特定模塊的task更加容易。但是沒有同時爲所有模塊運行同一task的選項,所以如果想這樣做的話,命令行接口仍然是更快的。

加速多模塊構建

當構建一個多模塊項目時,gradle依次處理所有模塊。隨着電腦的核越來越多,我們可以以並行的方式更快的構建進程。這個特性已存在於gradle中,但默認不可用。

如果想要把並行構建應用到項目中,需要在項目的根目錄下的gradle.properties文件中配置parallel屬性。

org.gradle.parallel=true

gradle基於可用的內核,嘗試選擇正確的線程數量。爲了阻止可能以並行的方式在相同模塊中執行兩個task導致的問題,每個線程都有一個完整的模塊。

並行構建是個隱藏的功能。這意味着它正在被繼續開發而且實現可能會隨時改變。這個特性剛成爲gradle的一部分,但已被廣泛使用。因此,可以設想它的實現不會徹底的消失或改變。

因人而異,通過簡單的啓動並行構建,可能會節約大量的構建時間。然而,有個問題。爲了並行構建高效工作,需要確定模塊之間是解耦的。

模塊耦合

正如我們在第二章看到的,在build.gradle文件中使用allprojects屬性可以爲項目中的所有模塊定義屬性。當你有個多模塊項目時,可以在任意模塊中使用allprojects應用屬性到項目中的所有模塊裏。gradle甚至使得在一個模塊中引用另一模塊中的屬性成爲可能。這些強大的特性使得多模塊構建的維護容易的多。但負面作用是模塊之間變得耦合。

兩個模塊只要它們訪問對方的task或屬性,就會被視爲耦合。這會有好幾種後果。例如,放棄了可移植性。如果你曾經決定從項目中提取library,在複製所有項目級設置前,是不可能構建library的。模塊耦合也會影響到並行構建。在任意模塊中使用allprojects將使得並行構建無效。當你給所有模塊添加項目級屬性前要意識到這些。

可以通過不直接訪問其他模塊的task或屬性避免耦合。如果需要的話,可以使用根模塊作爲中介以便模塊僅僅耦合到根模塊而非其他模塊。

總結

我們通過了解多模塊構建的結構開啓本章學習之旅。然後,瞭解瞭如何在單項目中建立多個模塊。我們也看到添加新的模塊影響到了構建task是如何運行的。

我們接着看到了一些新模塊的實際例子,以及它們都是怎麼被集成到一個項目中的。最後,我們提到一些在項目中更好的處理多模塊的要訣和技巧。

在下一章節,我們將會建立不同種類的測試,並會看到如何使用gradle使得運行這些測試更容易。我們將會不僅考慮直接在Java虛擬機上,而且還會在實體機和模擬器上運行單元測試

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章