前言
做為一個做Android的同學,我想對於“Gradle”是再熟悉不過了,但是對於Gradle卻是有點陌生,幾個月前,只是停留在這樣簡單的使用上。
compile “com.strange.unfamiliar:1.0”
複製程式碼
什麼讓我意識到其重要性呢?應該是在秋招完後,刷刷拉勾,看到杭州某創業公司對應屆Andoid 40k的誘惑,趕緊投了一波,投投投,然後收到了面試邀請,剛開始面,感覺這不按套路出牌啊,並不是很偏cs基礎,而是在工程實踐上,期間問到了gradle的問題,當時內心OS
勉強湊合了幾句之後,繼續追問,然後全程
面完之後要了面試官的聯絡方式,進行了一些溝通,意識到了其重要性,然後想著通過專案驅動來深入的學習一波,決定擼QQ空間熱修復實現方式中打補丁包的Gradle外掛,沒錯,就是要抄個nvwa。
so,準備出一系列文章來分享整個學習的過程。文章其實是從去年已經開始寫了兩篇,然後持續delay了,這裡重新撿起來。本篇將對Gradle的一些功能,重要性和其一些基礎做下講解,接下來,將進行Android專案中Gradle的講解,如何在AndroidStudio中使用Gradle來進行一些自定義構建。接著來進行一個簡單的外掛實現,最後著手來進行hotfix外掛的實現,共分為四個部分,希望在對自己的學習做一個總結的同時,能夠讓零基礎入門的Gradle的能夠跟隨部落格對Gradle有個認識,同時能夠實現一個簡單的Gradle外掛。
Gradle是什麼?
Gradle是一個基於Apache Ant和Apache Maven概念的專案自動化建構工具。它使用一種基於Groovy的特定領域語言來宣告專案設定,而不是傳統的XML。
那麼Gradle相比於Ant 和 Maven的構建方式,有那些優勢呢?
- 自動處理包相依關係 - 取自 Maven Repos 的概念
- 自動處理佈署問題 - 取自 Ant 的概念
- 條件判斷寫法直覺 - 使用 Groovy 語言
過去 Java 開發者常用 Maven 和 Ant 等工具進行封裝佈署的自動化,或是兩者兼用,不過這兩個包彼此有優缺點,如果頻繁改變相依包版本,使用 Ant 相當麻煩,如果瑣碎工作很多,Maven 功能不足,而且兩者都使用 XML 描述,相當不利於設計 if、switch 等判段式,即使寫了可讀性也不佳,而 Gradle 改良了過去 Maven、Ant 帶給開發者的問題,至今也成為 Android Studio 內建的封裝佈署工具。
Android中Gradle可以做什麼?
上一篇文章中講到了一個Android專案的構建過程,Android 構建系統編譯應用資源和原始碼,然後將它們打包成可供您測試、部署、簽署和分發的 APK。Android Studio 使用 Gradle 這一高階構建工具包來自動化執行和管理構建流程,同時也允許您定義靈活的自定義構建配置。每個構建配置均可自行定義一組程式碼和資源,同時對所有應用版本共有的部分加以重複利用。Android Plugin for Gradle 與這個構建工具包協作,共同提供專用於構建和測試 Android 應用的流程和可配置設定。
Gradle 和 Android 外掛獨立於 Android Studio 執行。這意味著,可以在 Android Studio 內、使用計算機上的命令列工具或在未安裝 Android Studio 的計算機(例如持續性整合伺服器)上構建 Android 應用。如果您不使用 Android Studio,可以學習如何從命令列構建和執行您的應用。無論您是從命令列、在遠端計算機上還是使用 Android Studio 構建專案,構建的輸出都相同。
如上圖所示,在一個Project中,除了我們專案自身的程式碼和資源之外,會有多個與專案構建相關的.gradle檔案,這些.Gradle檔案用來對於我們使用Gradle進行構建專案的整個過程中來使用。
Gradle中,每一個待編譯的工程都叫一個Project。每一個Project在構建的時候都包含一系列的Task。比如一個Android APK的編譯可能包含:Java原始碼編譯Task、資源編譯Task、JNI編譯Task、lint檢查Task、打包生成APK的Task、簽名Task等。
####Gradle工作流程
Gradle的工作流程如下圖所示,在每一個工作流程的前後,我們都可以進行一些hook操作,來滿足自己的需求。
Gradle工作包含三個階段:
- 首先是初始化階段。對我們前面的multi-project build而言,就是執行settings.gradle
- Initiliazation phase的下一個階段是Configration階段。
- Configration階段的目標是解析每個project中的build.gradle。比如multi-project build例子中,解析每個子目錄中的build.gradle。在這兩個階段之間,我們可以加一些定製化的Hook。這當然是通過API來新增的。
- Configuration階段完了後,整個build的project以及內部的Task關係就確定了。一個Project包含很多Task,每個Task之間有依賴關係。Configuration會建立一個有向圖來描述Task之間的依賴關係。所以,我們可以新增一個HOOK,即當Task關係圖建立好後,執行一些操作。
- 最後一個階段就是執行任務了。當然,任務執行完後,我們還可以加Hook。
簡言之,Gradle有一個初始化流程,這個時候settings.gradle會執行。 在配置階段,每個Project都會被解析,其內部的任務也會被新增到一個有向圖裡,用於解決執行過程中的依賴關係。然後才是執行階段。你在gradle xxx中指定什麼任務,gradle就會將這個xxx任務鏈上的所有任務全部按依賴順序執行一遍!
Gradle主要有三種物件
這三種物件和三種不同的指令碼檔案對應,在gradle執行的時候,會將指令碼轉換成對應的物件:
- Gradle物件:當我們執行gradle xxx或者什麼的時候,gradle會從預設的配置指令碼中構造出一個Gradle物件。在整個執行過程中,只有這麼一個物件。Gradle物件的資料型別就是Gradle。我們一般很少去定製這個預設的配置指令碼。
- Project物件:每一個build.gradle會轉換成一個Project物件。
- Settings物件:顯然,每一個settings.gradle都會轉換成一個Settings物件。
構建的生命週期,首先根據settings.gradle檔案構建出一個Seetings物件,然後根據Seetings中的配置,建立Project物件,去找各個project下的build.gradle檔案,根據檔案內容來對project物件進行配置。
一個project中Task的數量,取決於其中應用的插架的數目多少,通過
apply plugin: 'com.android.library'
複製程式碼
一個Task包含若干Action。所以,Task有doFirst和doLast兩個函式,用於新增需要最先執行的Action和需要和需要最後執行的Action。Action就是一個閉包。對於原有的Task,我們可以在其執行之前或者執行之後,進行一系列的Hook操作,在其執行之前和執行之後,新增一些操作。
tasks.getByName("task"){
it.doLast{
println "do the task"
}
}
複製程式碼
Groovy概述
這裡將對Groovy語言進行一個簡單的介紹,通過簡單地語法上的介紹,可以很好地看明白接下來對於構建過程中一些簡單地Groovy語法。通過簡短的介紹,可以很好地幫助我們看懂Gradle中的一些配置資訊。
Groovy是一種動態語言,基於Java並擴充了Java。 Java程式設計師可以無縫切換到使用Groovy開發程式。Groovy讓寫Java程式變得像寫指令碼一樣簡單。寫完就可以執行,Groovy內部會將其編譯成Java class然後啟動虛擬機器來執行。下圖是Groovy和Java程式碼和JVM的關係圖。
語言概述
- Groovy中支援動態型別,即定義變數的時候可以不指定其型別。(def不是必須的,但是為了程式碼清晰,建議還是使用def關鍵字)
def a = 5;
def b = "groovy"
複製程式碼
- 函式的定義,我們也無需進行引數型別的宣告,同時也可以不進行返回值型別的宣告,但是需要通過def欄位來定義,函式的最後一行作為返回值。
def function1(arg1, arg2) {
arg1 + arg2
}
String function2(str1, str2) {
return str1 + str2
}
複製程式碼
- 函式呼叫支援
引數名:引數值
方式呼叫
apply plugin: 'com.android.library'
複製程式碼
plugin:引數名,'com.android.library':引數值
- 強大字串支援功能
//單引號對應Java中字串
str1 = 'this string'
//雙引號,可通過$進行相應的轉譯
x = 1
str2 = "This is $x"
//通過換行實現每一行的間距
str3 = '''begin
line1
line2
end'''
複製程式碼
- 閉包
(英語:Closure),又稱詞法閉包(Lexical Closure)或函式閉包(function closures),是引用了自由變數的函式。這個被引用的自由變數將和這個函式一同存在,即使已經離開了創造它的環境也不例外。所以,有另一種說法認為閉包是由函式和與其相關的引用環境組合而成的實體。對於閉包的實現,從函數語言程式設計的角度來看就為了解決一個輸入對應一個輸出的問題。例如當我們想實現一個加法,我們必須通過傳遞兩個引數來實現,但是藉助於函數語言程式設計,我們可以做到只傳遞一個引數。
function plusAny(first) {
return function(second) {
return first + second;
}
}
var longLiveSeniorFunc = plusAny(1);
longLiveSeniorFunc(1);
複製程式碼
- Closure結構定義
def xxx = {paramters -> code}
def xxx = {無引數,純code}
複製程式碼
根據上述兩種結構,下面分別舉例
def closure = {
String param ->
println "This is $param"
}
複製程式碼
def closure = {
println 'This is closure'
}
複製程式碼
- 如何呼叫閉包
closure.call('Hello')
closure('Hello')
複製程式碼
閉包隱含一個自身引數it
def closure = {
println "This is $it"
}
複製程式碼
當閉包作為一個函式的引數時
def testClosure(Closure closure) {
closure()
}
testClosure(
println 'Test'
)
複製程式碼
總結
本文圍繞Gradle是什麼,可以做什麼,在Android中起到了什麼作用,然後是Gradle的工作流程,Gradle中使用語言Groovy的一個概述,幫助我們瞭解如何更好的使用Gradle。