這個週末的時間可以說都貢獻給gradle了,主要是做了什麼一個事情呢,也就是查閱各種gradle的資料,到現在還是很多東西沒搞清楚,但是也沒時間去一一的搞清楚了,再深如一點就要去各個梳理源碼了,根據我的這幾天的認識,這必將是一個較大的工程,所以暫時打住,這篇文章就來總結下這幾天我通過學習對gradle有的一些新的認知。
其實平時構建android項目,只需要網上搜搜官網找找,隨便配置下便可,但是這篇文章的目的並不是說明日常配置,而是對gradle配置的一些理解。
1、關於build.gradle文件的groovy語法合理性
我們知道build.gradle是用groovy語言寫的配置腳本,那麼既然是一門語言其便有固定的語法,爲啥build.gradle文件中的內容看來一點都不想我們平時寫的代碼呢。以project的build.gradle文件爲例
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
怎麼用goovy寫的gradle看起來這麼隨便,貌似都沒有一個正經的方法呢,這個是由於groovy語法比較靈活,當方法只有一個參數時,可以省略掉“()”這個符號,那麼allporjects對應的便實際上是
allprojects ({
repositories {
jcenter()
}
})
那麼allprojects參數是一個大括號括起來的醫堆東西又是啥,這就不得不提到groovy語言中的另一個概念了,那就是Closure這玩意相當於java中的匿名類,裏面可以有參數和方法。而這裏的repositories實際上就是一個方法,它同樣接收一個Closure閉包作爲參數。
然後可能有童鞋會說,這個groovy應該是面向對象的語言吧,那麼這個allprojects,還有什麼的buildscript是怎麼總得屬於什麼對象吧,的確是這樣的。答案就是 Project,當 build.gradle 執行時它會配置 Project 實例並將其設爲 Delegate 對象,即它的語句塊都會被委託給 Project 的實例,我們繼續查看 Project Doc,會發現 buildscript 被 ScriptHandler 委託了,接着查看 ScriptHandler Doc 會找到 repositories 和 dependencies 方法。
肯定又有同學要問了,怎麼又冒出一個Delegate對象,這又是啥玩意,這裏以一個例子來說明吧
class PostHandler {
int count
def info() { "This is Groovy Goodness post #$count!" }
}
def printInfo = {
count++
info()
}
task testClosure{
doLast{
printInfo.resolveStrategy = Closure.DELEGATE_FIRST
// Set delegate to Post object.
printInfo.delegate = new PostHandler(count: 100)
println "This is Groovy Goodness post #101!" == printInfo()
}
}
這裏定義的PostHandler實例就是printInfo這個Closure閉包的Delegate(委託),當吧Closure的resolveStrategy 屬性設置爲Closure.DELEGATE_FIRST的時候,執行閉包函數會執行到Delegate的方法,也就是說Closure 的 Delegate 機制可以使我們將一個閉包中的執行代碼的作用對象設置成任意其他對象。
到現在便能理解project的build.gradle文件在在groovy語法上的合理性了吧,於是可能又有同學拿了下面的代碼出來
apply plugin: 'com.android.application'
然後說我去,這是啥啊,apply看起來應該是個方法,那麼參數就是plugin: ‘com.android.application’,但是這個參數和我們知道的key:value的”key”:”value”這種格式不一樣啊,這就得提到 groovy 的 Named arguments 機制了,在goovy中Map可寫成name:”yoryky”的格式,key默認就是字符串的,value需鑰寫明字符類型,所以。。。原來是這樣子的。
好了,第一小節,算是說明了gradle的groovy語法合理性了。
2、gradle在android中的應用
第一小節提到的apply plugin: ‘com.android.application’這代碼,實際上是用於說明當前的build.gradle文件時用來配置android app的文件,對應項目模塊編譯後會生成一個apk文件,如果build.gradle文件使用apply plugin: ‘com.android.library’來配置,表示對應項目模塊是一個類庫,同時編譯時會生成一個aar文件。com.android.library 和 com.android.application 內容配置差不太多, 這裏以 com.android.application 爲例講解,在講解com.android.application之前,先來學一些基礎知識。
關於gradle這裏要搞清楚幾個概念,即Project、Task、Plugin、Property、Configuration。
關於Project第一小節有所提及,Project爲Task提供了執行上下文,所有的Plugin要麼向Project中添加用於配置的Property,要麼向Project中添加不同的Task。一個Task表示一個邏輯上較爲獨立的執行過程,比如編譯Java源代碼,拷貝文件,打包Jar文件,甚至可以是執行一個系統命令或者調用Ant。另外,一個Task可以讀取和設置Project的Property以完成特定的操作。
上面一段話貌似並不能讓我們搞清楚,這五個概念。
Project
關於Project這裏簡單介紹下,它是的gradle對應的默認構建對象,其中有buildscipt、allprojects等方法以及Property定義。
Task
Task對象可以說是我們使用gradle的核心,它就是發動機確保我們的gradle方法能夠執行起來,實際上我們看android studio的gradle工具,爲我們提供的app的task便能立刻明白Task的作用
點擊,這些task便能立即運行task定義的方法。
Property
property其實比較簡單,也就是對應我們java中的類的屬性,可以設置和獲取,但是在groovy中不通過set/get方法來設置獲取,而是直接通過參數名稱來完成設置獲取操作。可以用以下代碼來說明
android {
compileSdkVersion 26
buildToolsVersion "27.0.1"
defaultConfig {
applicationId "com.yoryky.mavendemo"
minSdkVersion 19
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
這裏的applicationId實際上會調用DefaultProductFlavor的以下方法
public ProductFlavor setApplicationId(String applicationId) {
this.mApplicationId = applicationId;
return this;
}
來對其mApplicationId這個Property進行賦值,當然這個例子不恰當(後面舉個例子彌補,哈哈哈,主要是突然想到,這裏的applicationId 、minSdkVersion 等實際上都是方法,我在這個地方一直有疑惑,覺得groovy沒有這樣設置字段的用法啊,沒曾想這裏原來是通過方法賦值)。
彌補的例子在這裏
class GroovyBean{
private name
}
def bean = new GroovyBean()
task printBean{
doLast{
bean.name = 'this is a bean'
println bean.name
}
}
這裏就是直接通過bean.name賦值了。
Plugin
上面的定義說了,所有的Plugin要麼向Project中添加用於配置的Property,要麼向Project中添加不同的Task,現在再看這句話應該比較清楚了吧。
Configuration
這玩意並不是什麼配置,實際上它是一個依賴集合,當配置compile方法的時候
compile 'com.android.support:appcompat-v7:26.+'
實際執行compile的時候會調用到DependencyHandler類的以下方法
/**
* Adds a dependency to the given configuration.
*
* @param configurationName The name of the configuration.
* @param dependencyNotation
*
* The dependency notation, in one of the notations described above.
* @return The dependency.
*/
Dependency add(String configurationName, Object dependencyNotation);
該方法會將configurationName->compile以及dependencyNotation->’com.android.support:appcompat-v7:26.+’封裝成一個Dependency對象並添加到Configuration依賴集中,等執行compile的時候,便會根據Configuration依賴集中的內容去添加不同的依賴(這個過程沒有看源碼,純粹個人理解,如有問題,請指正)。
到這裏五個概念算是理清了,那麼再來看看,這段代碼
apply plugin: 'com.android.application'
實際上這段代碼的作用就是引入’com.android.application’對應的AppPlugin的(這個插件是由gradle提供的),目的就是因爲我們要編譯並生成apk的一系列的task以及了Android 編譯、測試、打包等等的所有task。同時也要注意Project是gradle的默認構建對象,所以app的build.gradle的默認構建對象實際上也是Project實例。
3、gradle使用總結
第一小節講了build.gradle文件對於groovy語法的合理性,二第二小節講了gradle在android應用中涉及到的幾個概念。到這裏可以總結總結了。我認爲,gradle工具實際上可以看作Plugin包(個人認爲對應項目中的gradle插件版本)、groovy的編譯器以及運行管理器(個人認爲對應項目gradle編譯版本)
它在執行時並不會一開始便順序執行build.gradle文件中的內容,而是分兩個階段。
第一階段是配置階段,第二階段是實際的執行階段。
在配置階段,gradle將讀取所有的build.gradle的所有內容來配置project以及task
比如設置project和task的property,處理task之間的依賴關係,初始化表現property還是以下面代碼爲例說明
android {
compileSdkVersion 26
buildToolsVersion "27.0.1"
defaultConfig {
applicationId "com.yoryky.mavendemo"
minSdkVersion 19
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
通過applicationId方法,minSdkVersion等方法進行property的初始化,還通過
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:26.+'
testCompile 'junit:junit:4.12'
}
各種compile將依賴關係添加到Configuration依賴集中,待編譯的時候添加依賴集。
在執行階段,就是運行各種task來完成開發者目的
配置階段執行順利的話,實際上運行階段就是水到渠成的事情了,比我們執行的debug、compile、build、clean等實際上就是在執行階段。
關於gradle的知識有很多,本文按照自己的思維方式梳理了一下,總結出來就是以下三條
1、gradle.build依照groovy語法配置;
2、關於gradle在android應用中的的五大概念;
3、gradle是編譯器以及運行管理器;
參考文獻
這兩天看了不少的gradle相關的優秀文獻,有些文獻本文可能沒有直接參考,但是優秀的都放這裏吧,以便以後參考方便,不用到處找。
5、Gradle學習總結——根本上看透Android Studio構建
9、Gradle插件開發祕籍之斷點調試(基於Intellij)
11、Gradle Docs