Gradle學(xué)習(xí)總結(jié)——根本上看透Android Studio構(gòu)建

用過android studio的對gradle應(yīng)該都不陌生了,gradle文件的基本配置大同小異冬耿,略做了解使用應(yīng)該是沒什么問題了闲擦。但是深入細(xì)致的了解一下對于理解項(xiàng)目還是很有幫助的焕济,尤其是遇到一些配置復(fù)雜的github項(xiàng)目,不了解gradle可能會遇到跑不起來又束手無策的情形盔几。下面對gradle相關(guān)知識晴弃、用法做一下總結(jié)。

DSL (domain specific language)

即所謂領(lǐng)域?qū)S谜Z言逊拍,其基本思想是“求專不求全”上鞠,不像通用目的語言那樣目標(biāo)范圍涵蓋一切軟件問題,而是專門針對某一特定問題的計(jì)算機(jī)語言芯丧。
-DSL之于程序員正如伽南地之于以色列人芍阎,是最初也是最終的夢想。幾乎自計(jì)算機(jī)發(fā)明伊始缨恒,人們就開始談?wù)揇SL使用DSL了谴咸。
-前幾年迅速走紅的Ruby on Rails就被譽(yù)為“Web開發(fā)領(lǐng)域?qū)S谜Z言”

DSL 約等于 整潔的代碼

從概念上說,程序的編寫過程就是把業(yè)務(wù)領(lǐng)域中的問題通過代碼或者程序模型表達(dá)出來:
計(jì)算機(jī)的程序模型較為單一(歸根結(jié)底都是運(yùn)算和存儲)
在面向?qū)ο蠹夹g(shù)成為主流的今天骗露,通常情況下岭佳,計(jì)算機(jī)程序不太可能做到與業(yè)務(wù)領(lǐng)域中的概念一致,或者具有某些直覺的對應(yīng)萧锉。因此珊随,軟件的修改和可維護(hù)性并沒有想象中的容易。我們必須不斷地將業(yè)務(wù)領(lǐng)域中的概念轉(zhuǎn)換成相應(yīng)的代碼模型柿隙,然后再進(jìn)行修改叶洞。這種間接性直接造成了軟件的復(fù)雜度。
而DSL的主要目的就是要消除這樣的復(fù)雜度(或者說禀崖,以構(gòu)造DSL的復(fù)雜度代替這種復(fù)雜度)衩辟,DSL就要是要以貼近業(yè)務(wù)領(lǐng)域的方式來構(gòu)造軟件。因此帆焕,DSL的簡潔性往往是一種思維上的簡潔性惭婿,使我們不用費(fèi)太多的氣力就能看懂代碼所對應(yīng)的業(yè)務(wù)含義不恭。

DSL多以文本代碼的形式出現(xiàn)

多年來軟件工程實(shí)踐表明文本代碼是最有效率的編輯形式。但是一些特殊領(lǐng)域财饥,文本代碼并不是最佳的表現(xiàn)形式换吧,為了更好的貼近業(yè)務(wù)領(lǐng)域中的概念,我們可能會選擇使用一些圖形化的DSL钥星。如:[DSM(Domain Specific Modeling)工具GEMS(Generic Eclipse Modeling System)中就大量地使用了不同的圖形化的DSL來表述系統(tǒng)的各個(gè)不同側(cè)面沾瓦。

Gradle向我們提供了一整套DSL,所以在很多時(shí)候我們寫的代碼似乎已經(jīng)脫離了groovy谦炒,但是在底層依然是執(zhí)行的groovy
為了從命令行運(yùn)行g(shù)radle測試樣例贯莺,首先

配置環(huán)境變量

  1. 創(chuàng)建變量名:GRADLE_HOME ,變量值:
    C:\Users\jjx.gradle\wrapper\dists\gradle-2.5-all\d3xh0kipe7wr2bvnx5sk0hao8\gradle-2.5
  2. 加入path
    ;%GRADLE_HOME%\bin;
  3. 檢查,如下就ok宁改。
C:\Users\jjx>gradle -v
Gradle 2.5
Build time:   2015-07-08 07:38:37 UTC
Build number: none
Revision:     093765bccd3ee722ed5310583e5ed140688a8c2b
Groovy:       2.3.10
Ant:          Apache Ant(TM) version 1.9.3 compiled on December 23 2013
JVM:          1.7.0_75 (Oracle Corporation 24.75-b04)
OS:           Windows 7 6.1 amd64

初嘗禁果

在d:盤建個(gè)文件夾Test缕探,文件夾下建個(gè)文件build.gradle
打開文件,寫個(gè)簡單的代碼

task helloWorld << {
    println "Hello World"
}

打開cmd还蹲, d: 執(zhí)行g(shù)radle helloWorld

C:\Users\jjx>d:

D:\>cd Test

D:\Test>gradle helloWorld
:helloWorld
Hello World

BUILD SUCCESSFUL

Total time: 3.714 secs
D:\Test>

同時(shí)爹耗,這時(shí)候發(fā)現(xiàn)已經(jīng)自動在Test目錄下創(chuàng)建了.gradle文件。
上面helloWorld后的“<<”表示追加的意思谜喊,即向helloWorld中加入執(zhí)行過程
使用doLast可以達(dá)到同樣效果

task helloWorldTwo {
   doLast {
      println 'helloWorldTwo'}
}

如果需要向Task的最前面加入執(zhí)行過程潭兽,我們可以使用doFirst:

task helloWorldThree {
   doFirst {
      println 'helloWorldThree'}
}

耶,懂了斗遏!

關(guān)于 task

Gradle將當(dāng)前目錄下的build.gradle文件作為項(xiàng)目的構(gòu)建文件山卦。在上面的例子中,我們創(chuàng)建了一個(gè)名為helloWorld的Task诵次,在執(zhí)行g(shù)radle命令時(shí)账蓉,我們指定執(zhí)行這個(gè)helloWorld Task。這里的helloWorld是一個(gè)DefaultTask類型的對象,這也是定義一個(gè)Task時(shí)的默認(rèn)類型,當(dāng)然我們也可以顯式地聲明Task的類型峦嗤,甚至可以自定義一個(gè)Task類型
下面再看一個(gè)小case:
我們在Test文件夾下建一個(gè)src目錄,建一個(gè)dst目錄归敬,src目錄下建立一個(gè)文件,命名為here.txt
然后在build.gradle中append一個(gè)task:

task helloWorld << {
    println "Hello World"
}
task copyFile(type: Copy){
    from "src"
    into "dst"
}

代碼中(type:Copy)就是“顯式地聲明Task的類型”鄙早,helloworld沒有就是默認(rèn)得DefaultTask類型咯汪茧。

然后cmd中執(zhí)行命令

D:\Test>gradle copyFile
:copyFile

BUILD SUCCESSFUL

Total time: 2.645 secs
D:\Test>gradle copyFile
:copyFile

BUILD SUCCESSFUL

Total time: 3.361 secs
D:\Test>

好了! here.txt也跑到dst中去啦限番!簡單吧舱污!

加一把火,我們來看一下當(dāng)前目錄下(即Test目錄文件弥虐,這里也可以將這個(gè)目錄理解為一個(gè)project扩灯,不過還沒寫有生產(chǎn)力的代碼媚赖,哈哈哈)定義的task

D:\Test>gradle tasks
:tasks

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Help tasks
----------
components - Displays the components produced by root project 'Test'. [incubating]
dependencies - Displays all dependencies declared in root project 'Test'.
dependencyInsight - Displays the insight into a specific dependency in root project 'Test'.
help - Displays a help message.
model - Displays the configuration model of root project 'Test'. [incubating]
projects - Displays the sub-projects of root project 'Test'.
properties - Displays the properties of root project 'Test'.
tasks - Displays the tasks runnable from root project 'Test'.

Other tasks
-----------
copyFile
helloWorld

To see all tasks and more detail, run gradle tasks --all

To see more detail about a task, run gradle help --task <task>

BUILD SUCCESSFUL

Total time: 2.895 secs
D:\Test>

是不是很清晰呢,展示了各種task類型珠插,task的作用惧磺,以及另外兩個(gè)task相關(guān)的命令,相信聰明的你也一看就懂捻撑。

Gradle本身的領(lǐng)域?qū)ο笾饕蠵roject和Task磨隘。
Project為Task提供了執(zhí)行上下文,所有的Plugin要么向Project中添加用于配置的Property顾患,要么向Project中添加不同的Task番捂。
一個(gè)Task表示一個(gè)邏輯上較為獨(dú)立的執(zhí)行過程,比如編譯Java源代碼江解,拷貝文件设预,打包Jar文件,甚至可以是執(zhí)行一個(gè)系統(tǒng)命令或者調(diào)用Ant犁河。另外絮缅,一個(gè)Task可以讀取和設(shè)置Project的Property以完成特定的操作。是不是很屌的樣子呼股。
task關(guān)鍵字其實(shí)是一個(gè)groovy中方法的調(diào)用,該方法屬于Project画恰,而大括號之間的內(nèi)容則表示傳遞給task()方法的一個(gè)閉包彭谁。

task間的依賴

task之間是可以存在依賴關(guān)系,比如TaskA依賴TaskB允扇,那么在執(zhí)行TaskA時(shí)缠局,Gradle會先執(zhí)行TaskB,再執(zhí)行TaskA考润。我們可以在定義一個(gè)Task的同時(shí)聲明它的依賴關(guān)系:

task helloWorldFour(dependsOn:helloWorldThree) << {
    println 'hellohelloWorldFour'
}

或者

task helloWorldFour << {
    println 'hellohelloWorldFour'
}
helloWorldFour.dependsOn helloWorldThree
可配置的task

一個(gè)Task除了執(zhí)行操作之外狭园,還可以包含多個(gè)Property,其中有Gradle為每個(gè)Task默認(rèn)定義了一些Property糊治,比如description唱矛,logger等。
另外井辜,每一個(gè)特定的Task類型還可以含有特定的Property绎谦,比如Copy的from和to等。
當(dāng)然粥脚,我們還可以動態(tài)地向Task中加入額外的Property窃肠。在執(zhí)行一個(gè)Task之前,我們通常都需要先設(shè)定Property的值刷允。

task helloWorld << {
   description = "this is helloWorld" 
   println description
}

或者通過調(diào)用Task的configure()方法完成Property的設(shè)置:

task helloWorld << {
   println description
}
helloWorld.configure {
   description = "this is helloWorld" 
}
花式task
task showDescription1 << {
   description = 'this is task showDescription'
   println description
}

task showDescription2 << {
   println description
}
showDescription2.description = 'this is task showDescription'

task showDescription3 << {
   println description
}
showDescription3 {
   description = 'this is task showDescription'
}

對于每一個(gè)Task冤留,Gradle都會在Project中創(chuàng)建一個(gè)同名的Property碧囊,所以我們可以將該Task當(dāng)作Property來訪問,showDescription2便是這種情況纤怒。另外糯而,Gradle還會創(chuàng)建一個(gè)同名的方法,該方法接受一個(gè)閉包肪跋,我們可以使用該方法來配置Task歧蒋,showDescription3便是這種情況。
耶州既!簡單吧

關(guān)于 Groovy

Gradle是一種聲明式的構(gòu)建工具谜洽。
在執(zhí)行時(shí),Gradle并不會一開始便順序執(zhí)行build.gradle文件中的內(nèi)容吴叶,而是分為兩個(gè)階段阐虚,第一個(gè)階段是配置階段,然后才是實(shí)際的執(zhí)行階段蚌卤。
配置階段实束,Gradle將讀取所有build.gradle文件的所有內(nèi)容來配置Project和Task等,比如設(shè)置Project和Task的Property逊彭,處理Task之間的依賴關(guān)系等咸灿。
Gradle的DSL只是Groovy語言的內(nèi)部DSL,也必須遵循Groovy的語法規(guī)則侮叮。
Groovy語言中的兩個(gè)概念避矢,一個(gè)是Groovy中的Bean概念,一個(gè)是Groovy閉包的delegate機(jī)制囊榜。

bean

Groovy中的Bean和Java中的Bean有一個(gè)很大的不同审胸,即Groovy動態(tài)的為每一個(gè)字段都會自動生成getter和setter,并且我們可以通過像訪問字段本身一樣調(diào)用getter和setter

class GroovyBeanExample {
   private String name
}

def bean = new GroovyBeanExample()
bean.name = 'this is name' //實(shí)際調(diào)用的是"bean.setName('this is name')"
println bean.name  //實(shí)際調(diào)用的是
"println bean.getName()"

采用像直接訪問的方式的目的是為了增加代碼的可讀性卸勺,使它更加自然砂沛,而在內(nèi)部,Groovy依然
是在調(diào)用setter和getter方法曙求。

閉包的delegate機(jī)制

簡單來說碍庵,delegate機(jī)制可以使我們將一個(gè)閉包中的執(zhí)行代碼的作用對象設(shè)置成任意其他對象。

class Child {
   private String name
}
class Parent {
   Child child = new Child();
   void configChild(Closure c) {
      c.delegate = child
      c.setResolveStrategy Closure.DELEGATE_FIRST
      c()
   }
}

def parent = new Parent()
parent.configChild {
name = "child name"
}

println parent.child.name

在上面的例子中悟狱,當(dāng)調(diào)用configChild()方法時(shí)怎抛,并沒有指出name屬性是屬于Child的,但是它的確是在設(shè)置Child的name屬性芽淡。
事實(shí)上光從該方法的調(diào)用中马绝,我們根本不知道name是屬于哪個(gè)對象的,你可能會認(rèn)為它是屬于Parent的挣菲。
真實(shí)情況是富稻,在默認(rèn)情況下掷邦,name的確被認(rèn)為是屬于Parent的,但是我們在configChild()方法的定義中做了手腳椭赋,使其不再訪問Parent中的name(Parent也沒有name屬性)抚岗,而是Child的name。
在configChild()方法中哪怔,我們將該方法接受的閉包的delegate設(shè)置成了child宣蔚,然后將該閉包的ResolveStrategy設(shè)置成了DELEGATE_FIRST。這樣认境,在調(diào)用configChild()時(shí)胚委,所跟閉包中代碼被代理到了child上,即這些代碼實(shí)際上是在child上執(zhí)行的叉信。
此外亩冬,閉包的ResolveStrategy在默認(rèn)情況下是OWNER_FIRST,即它會先查找閉包的owner(這里即parent)硼身,如果owner存在硅急,則在owner上執(zhí)行閉包中的代碼。這里我們將其設(shè)置成了DELEGATE_FIRST佳遂,即該閉包會首先查找delegate(本例中即child)营袜,如果找到,該閉包便會在delegate上執(zhí)行丑罪。

聯(lián)想gradle中聲明的方法

在使用Gradle時(shí)连茧,我們并沒有像上面的parent.configChild()一樣指明方法調(diào)用的對象,而是在build.gradle文件中直接調(diào)用task()巍糯,apply()和configuration()等方法。這是
因?yàn)樵跊]有說明調(diào)用對象的情況下客扎,Gradle會自動將調(diào)用對象設(shè)置成當(dāng)前Project祟峦。
比如調(diào)用apply()方法和調(diào)用project.apply()方法的效果是一樣的。查查Gradle的Project文檔徙鱼,你會發(fā)現(xiàn)這些方法都是Project類的方法宅楞。
對于configurations()方法,該方法實(shí)際上會將所跟閉包的delegate設(shè)置成ConfigurationContainer袱吆,然后在該ConfigurationContainer上執(zhí)行閉包中的代碼厌衙。再比如,dependencies()方法绞绒,該方法會將所跟閉包的delegate設(shè)置成DependencyHandler婶希。

終于到了gradle

自定義Property

Gradle還為我們提供了多種方法來自定義Project的Property。

在build.gradle文件中定義Property

添加一個(gè)名為property1的Property:

ext.property1 = "this is property1"

或者采用閉包的形式

ext {
   property2 = "this is property2"
}

定義了Property后蓬衡,使用這些Property時(shí)我們則不需要ext喻杈,而是可以直接訪問:

task showProperties << {
   println property1
   println property2
}

還可以在執(zhí)行命令行的時(shí)候加屬性

task showCommandLieProperties << {
   println property3
}
//以下是cmd中執(zhí)行命令
gradle -Property3="this is property3" showCommandLieProperties

//通過JVM系統(tǒng)參數(shù)定義Property,與java類似彤枢,但是前面要約定以“org.gradle.project”為前綴
gradle -D org.gradle.project.property3="this is another property3" showCommandLieProperties

此外還可以通過環(huán)境變量來為Gradle設(shè)置Property,但是每一個(gè)Property都需要以“ORG_GRADLE_PROJECT_”為前綴:

ORG_GRADLE_PROJECT_property3="this is yet another property3"

Gradle 的 Plugin

Gradle最常用的Plugin便是java Plugin了筒饰。和其他Plugin一樣缴啡,java Plugin并沒有什么特別的地方,只是向Project中引入了多個(gè)Task和Property瓷们。當(dāng)然业栅,java Plugin也有比較與眾不同的地方,其中之一便是它在項(xiàng)目中引入了構(gòu)建生命周期的概念谬晕,就像Maven一樣碘裕。但是,和Maven不同的是固蚤,Gradle的項(xiàng)目構(gòu)建生命周期并不是Gradle的內(nèi)建機(jī)制娘汞,而是由Plugin自己引入的。

依賴管理

一個(gè)項(xiàng)目總會依賴于第三方夕玩,要么是一個(gè)第三方類庫你弦,要么是自己開發(fā)的另一個(gè)module
配置Gradle的Repository,就是告訴Gradle在什么地方去獲取這些依賴

repositories {
   mavenCentral()
   jCentral()
}

jCentral()是大于mavenCentral()的一個(gè)倉庫燎孟,現(xiàn)在是studio默認(rèn)的倉庫

Gradle對依賴進(jìn)行分組禽作,允許編譯時(shí)使用一組依賴,運(yùn)行時(shí)使用另一組依賴揩页。每一組依賴稱為一個(gè)Configuration旷偿,在聲明依賴時(shí),我們實(shí)際上是在設(shè)置不同的Configuration爆侣。

要定義一個(gè)Configuration萍程,我們可以通過以下方式完成:studio一般不需要設(shè)置,應(yīng)該是有默認(rèn)的兔仰,即為classpath

configurations {
   myDependency
}

通過dependencies()方法向myDependency中加入實(shí)際的依賴項(xiàng):

dependencies {
//下面的myDependency是關(guān)鍵
   myDependency 'org.apache.commons:commons-lang3:3.0'
}
//類似studio中的classpath
dependencies {
   classpath 'com.android.tools.build:gradle:1.3.0'
}
//還有 這里的compile茫负,testCompile
dependencies {
    compile project(':library')
    compile 'com.android.support:recyclerview-v7:22.2.1'
    compile 'com.android.support:design:22.2.1'
    compile 'com.evernote:android-intent:1.0.1'
    testCompile 'junit:junit:4.8.2' 
}

myDependency,classpath乎赴,compile忍法,testCompile都是Configuration(一組依賴)。
除了myDependency都不使我們定義的榕吼,為啥呢饿序,android Plugin會自動定義compile和testCompile分別用于編譯Java源文件和編譯Java測試源文件。classpath應(yīng)該是用于所有羹蚣,我類推的原探。
Gradle還允許我們聲明對其他Project或者文件系統(tǒng)的依賴。

dependencies {
//library是另一個(gè)module的名字
   compile project(':library')
}

對于本地文件系統(tǒng)中的Jar文件,我們可以通過以下方式聲明對其的依賴:

dependencies {
   //java
   compile files('spring-core.jar', 'spring-aap.jar')
   compile fileTree(dir: 'deps', include: '*.jar')
   //studio中一般這么寫
   compile fileTree(dir: 'libs', include: ['*.jar'])
}

構(gòu)建多module的project

Gradle為每個(gè)build.gradle都會創(chuàng)建一個(gè)相應(yīng)的module領(lǐng)域?qū)ο筇呦唬诰帉慓radle腳本時(shí)告匠,我們實(shí)際上是在操作諸如module這樣的Gradle領(lǐng)域?qū)ο蟆T诙鄊odule的項(xiàng)目中离唬,我們會操作多個(gè)module領(lǐng)域?qū)ο蠛笞āradle提供了強(qiáng)大的多module構(gòu)建支持
要創(chuàng)建多module的Gradle項(xiàng)目,我們首先需要在根(Root)Project中加入名為settings.gradle的配置文件输莺,該文件應(yīng)該包含各個(gè)子module(其實(shí)就是一個(gè)子project)的名稱戚哎。如setting.gradle中:

include 'library', 'demo'

類似module(子project)的build.gradle,(Root)Project也有自己的build.gradle嫂用,在里面通常設(shè)置:

allprojects {
    repositories {
        jcenter()
    }
    //通常studio項(xiàng)目沒有型凳,咱自己加的
   apply plugin: 'idea'
   task allTask << {
      println project.name
   }
}

allprojects()方法將repositories配置一次性地應(yīng)用于所有的module(子Project)和root-project本身,當(dāng)然也包括定義的Task嘱函,這個(gè)task配置到所有module里面了和root-project甘畅。

subprojects()方法用于配置所有的子Project(不包含根Project)

步入巔峰

Gradle本身只是一個(gè)架子,真正起作用的是Task和Plugin往弓。

自定義Task

Gradle中的Task要么是由不同的Plugin引入的疏唾,要么是我們自己在build.gradle文件中直接創(chuàng)建的。

  • 在build.gradle文件中直接定義
    需要定義的Task類型不多時(shí)
    Gradle其實(shí)就是groovy代碼函似,所以在build.gradle文件中槐脏,我們便可以定義Task類。
class HelloWorldTask extends DefaultTask {
    //@Optional撇寞,表示在配置該Task時(shí)顿天,message是可選的。
    @Optional
    String message = 'I am jjx'
    //@TaskAction表示該Task要執(zhí)行的動作,即在調(diào)用該Task時(shí)蔑担,hello()方法將被執(zhí)行
    @TaskAction
    def hello(){
        println "hello world $message"
    }
}

//hello使用了默認(rèn)的message值
task hello(type:HelloWorldTask)

//重新設(shè)置了message的值
task helloOne(type:HelloWorldTask){
   message ="I am a android developer"
}
  • 在當(dāng)前工程中定義Task類型
    只能應(yīng)用在當(dāng)前module中牌废,沒什么卵用,下面是全局可用的
  • 在單獨(dú)的項(xiàng)目中定義Task類型
    項(xiàng)目中存在大量的自定義Task類型時(shí),在另外的一個(gè)gradle文件中定義這些Task啤握,然后再apply到build.gradle文件中鸟缕。
    可以參考印象筆記的demo:https://github.com/evernote/evernote-sdk-android
    中的:
//這是插件
apply plugin: 'com.android.application'
//這里gradle-quality.gradle就是另外單獨(dú)定義了task的gradle
apply from: '../build-config/gradle-quality.gradle'
自定義Plugin

與自定義task極其類似,可以類推理解恨统,也是有3中方式定義,只是代碼不一樣:

apply plugin: DateAndTimePlugin

dateAndTime {
    timeFormat = 'HH:mm:ss.SSS'
    dateFormat = 'MM/dd/yyyy'
}

class DateAndTimePlugin implements Plugin<Project> {
    //該接口定義了一個(gè)apply()方法三妈,在該方法中畜埋,我們可以操作Project,
    //比如向其中加入Task畴蒲,定義額外的Property等悠鞍。
    void apply(Project project) {
        project.extensions.create("dateAndTime", DateAndTimePluginExtension)

        project.task('showTime') << {
            println "Current time is " + new Date().format(project.dateAndTime.timeFormat)
        }

        project.tasks.create('showDate') << {
            println "Current date is " + new Date().format(project.dateAndTime.dateFormat)
        }
    }
}
//每個(gè)Gradle的Project都維護(hù)了一個(gè)ExtenionContainer,
//我們可以通過project.extentions進(jìn)行訪問
//比如讀取額外的Property和定義額外的Property等。
//向Project中定義了一個(gè)名為dateAndTime的extension
//并向其中加入了2個(gè)Property咖祭,分別為timeFormat和dateFormat
class DateAndTimePluginExtension {
    String timeFormat = "MM/dd/yyyyHH:mm:ss.SSS"
    String dateFormat = "yyyy-MM-dd"
}

每一個(gè)自定義的Plugin都需要實(shí)現(xiàn)Plugin<T>接口掩宜,除了給Project編寫Plugin之外,我們還可以為其他Gradle類編寫Plugin么翰。
以上是在build.gradle文件中直接定義Plugin牺汤,還可以在當(dāng)前工程中、單獨(dú)的項(xiàng)目中創(chuàng)建Plugin浩嫌,一般情況不需要了解檐迟。

怎么樣,通過以上學(xué)習(xí)码耐,再看android studio的gradle代碼是不是小case了呢追迟!

下篇有機(jī)會一句一句分析studio 項(xiàng)目里常用的gradle代碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市骚腥,隨后出現(xiàn)的幾起案子敦间,更是在濱河造成了極大的恐慌,老刑警劉巖束铭,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件廓块,死亡現(xiàn)場離奇詭異,居然都是意外死亡纯露,警方通過查閱死者的電腦和手機(jī)剿骨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來埠褪,“玉大人浓利,你說我怎么就攤上這事〕伲” “怎么了贷掖?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長渴语。 經(jīng)常有香客問我苹威,道長,這世上最難降的妖魔是什么驾凶? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任牙甫,我火速辦了婚禮,結(jié)果婚禮上调违,老公的妹妹穿的比我還像新娘窟哺。我一直安慰自己,他們只是感情好技肩,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布且轨。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪旋奢。 梳的紋絲不亂的頭發(fā)上泳挥,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天,我揣著相機(jī)與錄音至朗,去河邊找鬼屉符。 笑死,一個(gè)胖子當(dāng)著我的面吹牛爽丹,可吹牛的內(nèi)容都是我干的筑煮。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼粤蝎,長吁一口氣:“原來是場噩夢啊……” “哼真仲!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起初澎,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤秸应,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后碑宴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體软啼,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年延柠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了祸挪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,133評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡贞间,死狀恐怖贿条,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情增热,我是刑警寧澤整以,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站峻仇,受9級特大地震影響公黑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜摄咆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一凡蚜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吭从,春花似錦朝蜘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至鸭廷,卻和暖如春枣抱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背辆床。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工佳晶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人讼载。 一個(gè)月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓轿秧,卻偏偏與公主長得像,于是被迫代替她去往敵國和親咨堤。 傳聞我的和親對象是個(gè)殘疾皇子菇篡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評論 2 355

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)一喘,斷路器驱还,智...
    卡卡羅2017閱讀 134,672評論 18 139
  • 本篇主要是個(gè)人學(xué)習(xí)gradle的筆記總結(jié) 一.開始之前 1. 為什么學(xué)習(xí)Gradle 采用DSL(Doma...
    zyq_neuq閱讀 1,506評論 2 12
  • 這篇文章講給大家?guī)韌radle打包系列中的高級用法-自己動手編寫gradle插件。我們平常在做安卓開發(fā)時(shí)凸克,都會在...
    呆萌狗和求疵喵閱讀 15,986評論 22 80
  • 是什么议蟆? 在語法上是基于Groovy語言的(Groovy 是一種基于JVM的敏捷開發(fā)語言,可以簡單的理解為強(qiáng)類型語...
    千山萬水迷了鹿閱讀 99,569評論 4 122
  • 昨晚實(shí)在無聊萎战,半夜爬起來看BBC的《數(shù)學(xué)的故事》咐容。當(dāng)那個(gè)長得像Daniel Craig的大叔以極其詭異的手法解決了...
    Pope怯懦懦地閱讀 904評論 0 0