GRADLE腳本的語法和BUILD流程

導(dǎo)語

Android Studio中使用了Gradle進(jìn)行build咽笼。我閱讀了groovy官方文檔箩张,Gradle官方文檔及一些源代碼,Android插件的官方文檔及一些源代碼,希望給大家介紹一下Gradle腳本的語法和處理流程。簡單Groovy是一種運(yùn)行在JVM上的語言猿规, Gradle是使用Groovy實(shí)現(xiàn)的, Android插件提供了android特定的功能宙橱。

1. Gradle腳本的build流程

1.1Groovy和腳本

Groovy會(huì)把腳本編譯成groovy.lang.Script的子類坎拐。groovy.lang.Script是一個(gè)抽象類,它有一個(gè)抽象方法run(). 如果有一個(gè)腳本的文件名是Main养匈,它的內(nèi)容是:

println 'Hello from Groovy'

它編譯后生成的類是:

class Main extends groovy.lang.Script { 
  def run() { 
    println 'Hello from Groovy' 
}

static void main(String[] args) { 
    InvokerHelper.runScript(Main, args) 
}

腳本中的語句會(huì)成為run方法的實(shí)現(xiàn)哼勇。

Gradle腳本編譯生成的類當(dāng)然也繼承自groovy.lang.Script,并同時(shí)實(shí)現(xiàn)了Gradle自己的script接口org.gradle.api.Script呕乎。

1.2 Gradle腳本對(duì)象的代理對(duì)象

每一個(gè)Gradle腳本對(duì)象都有一個(gè)代理對(duì)象积担。Settings腳本的代理對(duì)象是Setting對(duì)象,Build腳本的代理對(duì)象是Project對(duì)象猬仁。每一個(gè)在腳本對(duì)象未找到的屬性和方法都會(huì)轉(zhuǎn)到代理對(duì)象帝璧。

Groovy和Java的一個(gè)不同之處就是Groovy可以動(dòng)態(tài)的添加方法和屬性。動(dòng)態(tài)添加的方式之一是覆蓋propertyMissing和methodMissing方法湿刽,Gradle就是采用這種方式實(shí)現(xiàn)了腳本對(duì)象的代理對(duì)象的烁。下面是Gradle腳本對(duì)象的基類BasicScript的實(shí)現(xiàn)代碼片段:

public abstract class BasicScript extends org.gradle.groovy.scripts.Script implements org.gradle.api.Script, FileOperations, ProcessOperations {

......

private Object target;

private DynamicObject dynamicTarget;

 ......

public Object propertyMissing(String property) {

  if ("out".equals(property)) {

      return System.out;

  } else {

      return dynamicTarget.getProperty(property);

  }

}

 ......

public Object methodMissing(String name, Object params) {

  return dynamicTarget.invokeMethod(name, (Object[])params);

}

}

1.3 Gradle腳本的build流程

Gradle腳本的build流程分為3個(gè)階段:

(1)初始化階段

Gradle支持單個(gè)和多個(gè)工程的編譯。在初始化階段诈闺,Gradle判斷需要參與編譯的工程渴庆,為每個(gè)工程創(chuàng)建一個(gè)Project對(duì)象,并建立工程之間的層次關(guān)系雅镊。這個(gè)階段執(zhí)行Settings腳本襟雷。

(2)配置階段

Gradle對(duì)上一步創(chuàng)建的Project對(duì)象進(jìn)行配置。這個(gè)階段執(zhí)行Build腳本

(3)執(zhí)行階段

Gradle執(zhí)行選中的task仁烹。

1.4一個(gè)demo

下面是demo工程的文件層次耸弄,這個(gè)demo會(huì)被后面的部分使用。這個(gè)例子包含一個(gè)app子工程和一個(gè)library子工程卓缰。settings.gradle是Setttings腳本计呈,三個(gè)build.gradle都是Build腳本砰诵。

--settings.gradle
--build.gradle
--app
--build.gradle
--mylibrary
--build.gradle

2. Settings腳本

2.1 Settings腳本的內(nèi)容

Settings腳本通常比較簡單,用于初始化project樹捌显。demo中settings.gradle的內(nèi)容是:

include ':app', ':mylibrary'

2.2 groovy的相關(guān)語法

groovy允許省略語句結(jié)尾的分號(hào)茁彭,并允許在方法調(diào)用時(shí)省略括號(hào),上面的代碼等價(jià)于:

include(':app', ':mylibrary');

2.3 Gradle的相關(guān)語法

初始化腳本的Script對(duì)象會(huì)有一個(gè)Project代理對(duì)象苇瓣。在Script對(duì)象沒有定義的屬性和方法調(diào)用就會(huì)被轉(zhuǎn)到Project對(duì)象尉间。上面的語句實(shí)際上調(diào)用的是Project對(duì)象的include方法,該方法的原型如下:

void include(String[] projectPaths)

這個(gè)方法將給定的工程添加到build中击罪。工程路徑的格式是: 以一個(gè)可選的”:”的開始哲嘲,它表示”:”前面有一個(gè)不需要名字的根工程;剩下的部分是以”:”分隔的工程名媳禁。例如眠副, “:app”中”:”的是可選的,它表示”:”前面有一個(gè)不需要名字的根工程竣稽。

運(yùn)行”gradle projects”可以獲得這個(gè)demo的project樹:

Root project 'AndroidGradleDemo'
+--- Project ':app'
\--- Project ':mylibrary'

3. Build腳本

3.1 app工程的Build腳本

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
......

    buildTypes {
        debug {
            applicationIdSuffix ".debug"
     }

    release {
        minifyEnabled false
        //getDefaultProguardFile() will return the full path of 'proguard-android.txt'
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
  }
}

.......

3.2 Groovy相關(guān)的語法

1)Map

Groovy可以直接使用Java中的各種集合類型囱怕,例如Map和List等等。在初始化語法上有一些擴(kuò)展

def key = 'name'

//創(chuàng)建一個(gè)Map毫别。注意"Guillaume"的key是字符串"key"而不是變量key的值

def person = [key: 'Guillaume'] 



assert !person.containsKey('name') 

assert person.containsKey('key') 



//創(chuàng)建一個(gè)Map娃弓。我們使用()包圍了key。這種情況下岛宦,"Guillaume"的key是變量key的值"name"

def person2 = [(key): 'Guillaume'] 



assert person2.containsKey('name') 

assert !person2.containsKey('key')

(2)閉包

(2.1)Syntax

Groovy中的閉包是一個(gè)匿名的代碼塊台丛。它的語法規(guī)則是:

{ [closureParameters -> ] statements }

)[closureParameters→]是可選的以”,”分隔的參數(shù)列表

*)statements是0或多條Groovy語句

下面是閉包的一些例子:

{ item++ } 



//A closure using an implicit parameter (it)

{ println it } 



//In that case it is often better to use an explicit name for the parameter

{ it -> println it } 



{ String x, int y -> 

  println "hey ${x} the value is ${y}"

}



{ reader -> 

  def line = reader.readLine()

  line.trim()

}

(2.2)Owner, delegate and thisGroovy中的閉包有三個(gè)重要的概念: Owner, delegate and this

*)this是指定義閉包的類

*)Owner是指直接包含閉包的類或閉包

*)delegate是指用于解析閉包中屬性和方法調(diào)用的第三方對(duì)象

下面的代碼段說明了this和閉包的區(qū)別:

class Enclosing {

  void run() {

  def whatIsOwnerMethod = { getOwner() } 

  //calling the closure will return the instance of Enclosing where the the           closure is defined 

  assert whatIsOwnerMethod() == this 

  def whatIsOwner = { owner } 

  assert whatIsOwner() == this 

  }

}

class NestedClosures {

    void run() {

    def nestedClosures = {

    def cl = { owner } 

    cl()

}

//then owner corresponds to the enclosing closure, hence a different object from this!

  assert nestedClosures() == nestedClosures

}

}

下面的代碼演示了delegate的作用。

class Person {

  String name

}

def p = new Person(name:'Igor')

def cl = { name.toUpperCase() } 

cl.delegate = p

//在設(shè)置了delegate后砾肺,閉包中的name屬性被解析成作為delegate的Person的屬性 

assert cl() == 'IGOR'

Gradle中大量使用閉包的delegate來實(shí)現(xiàn)對(duì)各種對(duì)象的配置挽霉。

(3)轉(zhuǎn)化成Java語法后的代碼

Map<String, Object> map = new HashMap<String, Object>();

map.put("plugin", "com.android.appliaction"); 

apply(map);//這個(gè)方法會(huì)代理給Project對(duì)象



android({

  //這個(gè)閉包的delegate是Android插件添加的extension



  compileSdkVersion(23);

  ......



  buildTypes({

  //這個(gè)閉包的delegate是    NamedDomainObjectContainerConfigureDelegate.



  debug({

      applicationIdSuffix(".debug")

  });



release({

minifyEnabled(false);

  //getDefaultProguardFile() will return the full path of 'proguard-  android.txt'

    proguardFiles(getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro');

    });

  });

});

3.3 Gradle的相關(guān)語法

(1)Project查找方法的規(guī)則

Build腳本的代理對(duì)象是Project對(duì)象。Project對(duì)象從6個(gè)范圍中查找方法:

*)Project對(duì)象本身定義的方法

*)腳本文件中定義的方法

*)被插件添加的extension. extension的名字可以做為方法名

*)被插件添加的convension方法变汪。

*)工程中的task侠坎。task的名字可以作為方法名

*)父工程中的方法。

(2)apply plugin: ‘com.android.application’

“apply plugin: ‘com.android.application’”等價(jià)于調(diào)用Project對(duì)象的apply方法裙盾,該方法的原型是:

void apply(Map<String,?> options)

Project.apply方法用于應(yīng)用腳本和插件实胸。我們指定了鍵值對(duì)”plugin”:”com.android.application”, 因此這個(gè)調(diào)用會(huì)添加Android插件。Android插件會(huì)添加一個(gè)名字是”android”闷煤,類型是com.android.build.gradle.AppExtension的extension.

(3)android{…}

這個(gè)代碼塊等價(jià)于”android({…});”, 即調(diào)用了一個(gè)方法名是”android”的方法童芹,該方法的參數(shù)是一個(gè)閉包。當(dāng)調(diào)用Project對(duì)象的android方法時(shí)鲤拿,實(shí)際上是找到了名字是”android”的extension, 把這個(gè)extension設(shè)為閉包的代理并運(yùn)行這個(gè)閉包署咽。從而閉包中的方法調(diào)用實(shí)際上都是對(duì)com.android.build.gradle.AppExtension的調(diào)用近顷。這就是上面提到的Project查找方法的規(guī)則中的一條:被插件添加的extension的名字可以做為方法名生音。

*)查找extension的邏輯是在ExtensionsStoage.configureExtension中,代碼如下

public <T> T configureExtension(String methodName, Object ... arguments) {

  Closure closure = (Closure) arguments[0];

  ClosureBackedAction<T> action = new ClosureBackedAction<T>(closure);

  //根據(jù)名字查找extension

  ExtensionHolder<T> extensionHolder = extensions.get(methodName);

      return extensionHolder.configure(action);//Line 69

}

*)設(shè)置閉包的delegate并運(yùn)行閉包的邏輯是在ClosureBackedAction.execute中, 代碼如下:

  public void execute(T delegate) {

      if (closure == null) {

      return;

  }



  try {

        if (configureableAware && delegate instanceof Configurable) {

        ((Configurable) delegate).configure(closure);

  } else {

      Closure copy = (Closure) closure.clone();

      copy.setResolveStrategy(resolveStrategy);

      //設(shè)置delegate

      copy.setDelegate(delegate);

      if (copy.getMaximumNumberOfParameters() == 0) {

            copy.call();

      } else {

        //運(yùn)行閉包

      copy.call(delegate);//

      }

  }

  } catch (groovy.lang.MissingMethodException e) {

      if (Objects.equal(e.getType(), closure.getClass()) &&         Objects.equal(e.getMethod(), "doCall")) {

      throw new InvalidActionClosureException(closure, delegate);

   }

    throw e;

  }

}

(4)buildTypes{…}

buildTypes{…}位于android{…}代碼塊中窒升,它等價(jià)于AppExtension的buildTypes方法缀遍,該方法的參數(shù)是一個(gè)閉包。AppExtension中定義了一個(gè)buildTypes方法饱须,代碼如下:

void buildTypes(Action<? super     NamedDomainObjectContainer<DefaultBuildType>> action) {

  plugin.checkTasksAlreadyCreated();

  action.execute(buildTypes)

}

buildTypes的閉包的delegate是一個(gè)NamedDomainObjectContainerConfigureDelegate類型的實(shí)例域醇,因此該閉包內(nèi)部的方法都會(huì)被delegate到這個(gè)對(duì)象,諸如”debug”方法蓉媳,”release”方法譬挚,或者其他的以用戶自定義的build type作為名字的方法。相應(yīng)的代碼是在NamedDomainObjectContainerConfigureDelegate的基類ConfigureDelegate的invokeMethod中酪呻,代碼如下:

public Object invokeMethod(String name, Object paramsObj) {

......

  //對(duì)已經(jīng)創(chuàng)建過的build type進(jìn)行配置

_delegate.invokeMethod(name, result, params);

if (result.isFound()) {

    return result.getResult();

}



MissingMethodException failure = null;

if (!isAlreadyConfiguring) {

    // Try to configure element

try {

//創(chuàng)建新的build type并進(jìn)行配置

    _configure(name, params, result);

 } catch (MissingMethodException e) {

// Workaround for backwards compatibility. Previously, this case would unintentionally cause the method to be invoked on the owner

// continue below

failure = e;

}

if (result.isFound()) {

    return result.getResult();

}

}
}

*)對(duì)于已經(jīng)創(chuàng)建過的build type减宣,調(diào)用_delegate.invokeMethod(),進(jìn)而調(diào)用DefaultNamedDomainObjectCollection$ContainerElementsDynamicObject.invokeMethod()進(jìn)行配置

*)對(duì)于需要?jiǎng)?chuàng)建的build type,調(diào)用_configure(), 進(jìn)而調(diào)用AbstractNamedDomainObjectContainer.create(String name, Closure configureClosure)進(jìn)行創(chuàng)建和配置


如果大家喜歡玩荠,請(qǐng)不吝打賞F犭纭!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末阶冈,一起剝皮案震驚了整個(gè)濱河市闷尿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌女坑,老刑警劉巖填具,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異堂飞,居然都是意外死亡灌旧,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門绰筛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來枢泰,“玉大人,你說我怎么就攤上這事铝噩『饴欤” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵骏庸,是天一觀的道長毛甲。 經(jīng)常有香客問我,道長具被,這世上最難降的妖魔是什么玻募? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮一姿,結(jié)果婚禮上七咧,老公的妹妹穿的比我還像新娘跃惫。我一直安慰自己,他們只是感情好艾栋,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布爆存。 她就那樣靜靜地躺著,像睡著了一般蝗砾。 火紅的嫁衣襯著肌膚如雪先较。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天悼粮,我揣著相機(jī)與錄音闲勺,去河邊找鬼。 笑死矮锈,一個(gè)胖子當(dāng)著我的面吹牛霉翔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播苞笨,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼债朵,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了瀑凝?” 一聲冷哼從身側(cè)響起序芦,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎粤咪,沒想到半個(gè)月后谚中,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡寥枝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年宪塔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片囊拜。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡某筐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出冠跷,到底是詐尸還是另有隱情南誊,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布蜜托,位于F島的核電站抄囚,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏橄务。R本人自食惡果不足惜幔托,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蜂挪。 院中可真熱鬧柑司,春花似錦迫肖、人聲如沸锅劝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽故爵。三九已至玻粪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間诬垂,已是汗流浹背劲室。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留结窘,地道東北人很洋。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像隧枫,于是被迫代替她去往敵國和親喉磁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,099評(píng)論 25 707
  • 這篇文章講給大家?guī)韌radle打包系列中的高級(jí)用法-自己動(dòng)手編寫gradle插件官脓。我們平常在做安卓開發(fā)時(shí)协怒,都會(huì)在...
    呆萌狗和求疵喵閱讀 15,985評(píng)論 22 80
  • Gradle是基于Groovy的動(dòng)態(tài)DSL,而Groovy是基于JVM的卑笨,Groovy的語法和Java很類似孕暇。 C...
    HoooChan閱讀 7,470評(píng)論 0 7
  • Gradle對(duì)于很多開發(fā)者來說有一種既熟悉又陌生的感覺,他是離我們那么近赤兴,以至于我每天做項(xiàng)目都需要他妖滔,但是他又是離...
    阿_希爸閱讀 9,575評(píng)論 10 199
  • 今年的元宵節(jié)除了少了吵鬧的鞭炮聲外,與往年無異桶良,耳邊倒是清凈了不少座舍,照例蜷縮在自己的床頭,劃拉著手機(jī)艺普,看央視...
    懂我笑容任閱讀 366評(píng)論 0 1