Gradle實(shí)戰(zhàn)讀書(shū)筆記之一 Gradle 構(gòu)建塊

Gradle的一些命令記錄:

  1. 執(zhí)行一個(gè)指定的任務(wù):
    gradle -q taskName // 如:gradle -q hello
  2. 查看所有任務(wù)
    gradle -q tasks
  3. 執(zhí)行多個(gè)任務(wù)
    gradle hello hello2
  4. 排除任務(wù)的執(zhí)行 -x:
    gradle hello -x hello2 // 排除hello2執(zhí)行

org.gradle.api.Project 項(xiàng)目

在Gradle中溯乒,一個(gè)項(xiàng)目(project)表示一個(gè)正在構(gòu)建的組件(如:一個(gè)jar文件夹厌,android中,一個(gè)aar文件)裆悄;
Gradle的build.gradle文件相當(dāng)于Maven的pom.xml矛纹,每個(gè)build.gradle文件在構(gòu)建啟動(dòng)后,對(duì)應(yīng)于org.gradle.api.Project的一個(gè)實(shí)例光稼,并且能夠通過(guò)project變量使其隱式可用或南;

Project類接口

一個(gè)Project可用創(chuàng)建新的task,添加依賴與配置钟哥,實(shí)際上這些都是 project接口的方法實(shí)現(xiàn)的迎献,如下dependencies 就是調(diào)用 Project的 dependencies 方法,并傳遞閉包腻贰;

dependencies {
    provided 'com.android.support:support-annotations:25.3.1'
    provided 'com.android.support:support-v4:25.3.1'
}

構(gòu)建Project實(shí)例中,可通過(guò)代碼訪問(wèn)Gradle的所有特性扒秸,如:task創(chuàng)建播演、依賴管理等;當(dāng)訪問(wèn)屬性與方法時(shí)伴奥,不需要使用project變量写烤;

setDescription("myProject")  // 沒(méi)有顯示指定 project變量
println "Description of project $name: " + project.description;  // 訪問(wèn)name與description屬性;

在Android開(kāi)發(fā)中拾徙,每個(gè)module都表示一個(gè)Project洲炊,有自己獨(dú)立的 build.gradle腳本;

org.gradle.api.Task 任務(wù)

任務(wù)task對(duì)應(yīng)的是 org.gradle.api.Task接口尼啡;任務(wù)的一些重要功能有:任務(wù)動(dòng)作(task action)和任務(wù)依賴(task dependency)暂衡;
任務(wù)動(dòng)作:定義了一個(gè)當(dāng)任務(wù)執(zhí)行時(shí)最小的工作單元;
任務(wù)依賴:有些時(shí)候崖瞭,某個(gè)任務(wù)的運(yùn)行之前需要其他Task先運(yùn)行狂巢;

Task接口

屬性

每個(gè)ProjectTask實(shí)例都提供可通過(guò) getter與setter方法訪問(wèn)的屬性,如:項(xiàng)目名书聚,任務(wù)名等唧领;
通常藻雌,我們需要自定義一些自己的屬性;比如:Android的編譯版本斩个,minSdk等等胯杭;這就用到擴(kuò)展屬性;

擴(kuò)展屬性
在內(nèi)部受啥,這些屬性以鍵值對(duì)的形式存儲(chǔ)歉摧,添加屬性,使用 ext命名空間腔呜,在定義擴(kuò)展屬性的時(shí)候叁温,需要使用到 ext 命名空間前綴,如下 :

// 此擴(kuò)展屬性信息核畴,定義在一個(gè)單獨(dú)的gradle文件中膝但,使用的使用
// 在項(xiàng)目的根build.gralde文件中 apply from: "文件名"來(lái)引用;

// gradle配置信息
ext {
    COMPILE_SDK_VERSION = 25           // 25 23
    BUILD_TOOLS_VERSION = "25.0.2"   // 25.0.2 23.0.2

    MIN_SDK_VERSION = 14
    TARGET_SDK_VERSION = 23     // 這個(gè)千萬(wàn)不能改

    // support依賴支持包
    compile_support = [
            design      : "com.android.support:design:25.3.1",
            annotations : "com.android.support:support-annotations:25.3.1",
            appcompat   : "com.android.support:appcompat-v7:25.3.1",
            recyclerview: "com.android.support:recyclerview-v7:25.3.1",
    ]

    // 常用庫(kù)包
    compile_common = [
            gson: "com.google.code.gson:gson:2.7"
    ]
}

使用的時(shí)候谤草,可省略ext前綴跟束,如下:

android {
    compileSdkVersion COMPILE_SDK_VERSION
    buildToolsVersion BUILD_TOOLS_VERSION
    ....

Gradle屬性
Gradle屬性可通過(guò)gradle.properties文件中聲明直接添加到項(xiàng)目中,此文件位于 <USER_HOME>/.gradle.properties目錄 或 項(xiàng)目的根目錄下丑孩,這些屬性可以通過(guò)項(xiàng)目實(shí)例訪問(wèn)冀宴;
如果一個(gè)project有多個(gè)module,也只能有一份 gradle.properties文件温学;

project下的gradle屬性文件

比如,在properties文件聲明了(最好在本項(xiàng)目的文件做實(shí)驗(yàn)略贮,不動(dòng)全局的,見(jiàn)下解釋):
myname=zhaoyubetter
通過(guò)直接引用名字來(lái)訪問(wèn)
println(myname) // 來(lái)進(jìn)行訪問(wèn)仗岖;

注意:在android studio 中逃延,android視圖中,顯示的gradle.properties文件是全局的轧拄,與操作系統(tǒng)的是同一個(gè)文件揽祥,如下:

gradle.properties全局文件

項(xiàng)目中的 gradle.properties文件需要切換到 project files 視圖,才能看到檩电,如下:

項(xiàng)目的gradle.properties文件

使用Task

默認(rèn)情況下拄丰,每個(gè)新創(chuàng)建的Task都是org.gradle.api.DefaultTask類型的;

聲明task動(dòng)作
動(dòng)作(action)就是在task中合適的地方放置構(gòu)建邏輯俐末,Task接口提供2個(gè)相關(guān)的方法來(lái)聲明task動(dòng)作:doFirst(Closure) (可使用 >>)與 doLast(Closure) (可使用<<左移)料按;

def curVersion = '1.0.0-SNAPSHOT'
task printVersion {
    doLast {
        println ">>>>>>>>>>>>>>>>>>verson:${curVersion}"
    }
}

// or 寫(xiě)成
task printVersion << {
     println ">>>>>>>>>>>>>>>>>>verson:${curVersion}"
}

通過(guò) gradle printVersion 可執(zhí)行此任務(wù);

訪問(wèn)DefaultTask屬性

  • description屬性:描述任務(wù)的作用鹅搪;
  • group屬性: 用于定義task的邏輯分組站绪;
    創(chuàng)建Task時(shí),可傳遞丽柿,作為參數(shù):
def version = '1.0.0-SNAPSHOT'
task printVersion(group: 'versioning', description: 'Print project version') {
    doFirst {
        println ">>>>> before reading the version"
    }
    doLast {
        println ">>>>> version: $version"
    }
}

// 或者
task printVersion() {
    group = 'versioning'
    description =  'Print project version'

定義Task依賴

dependsOn方法恢准,允許聲明依賴一個(gè)或多個(gè)task魂挂,通過(guò)task依賴關(guān)系圖來(lái)建模完整的task生命周期;

def group = 'myGroup'
task first << { println ">>>> first" }
task second << { println ">>>> second" }
// 依賴多個(gè)任務(wù)
task printVersion(group: group, dependsOn: [second, first]) << {
    println ">>>> the version : $version"
}
task third << { println ">>> third" }
third.dependsOn('printVersion')  // 聲明依賴時(shí)馁筐,按名稱引用task

輸入:gradle -q third, 輸出如下:

依賴輸出涂召,順序是亂的

task 依賴的執(zhí)行順序
Gradle不能保證依賴的執(zhí)行順序,dependsOn只是定義了所依賴的task需要先執(zhí)行敏沉;在gradle中果正,執(zhí)行順序是由task的輸入/輸出規(guī)范自動(dòng)確定的;

終結(jié)器Task

使用 task 的finalizedBy來(lái)使用一個(gè)特定的終結(jié)器:

task first << { println ">>>> first" }
task second << { println ">>>> second" }
first.finalizedBy second        // 執(zhí)行 gradle -q first 時(shí)盟迟,會(huì)觸發(fā) task second

添加任意代碼秋泳,設(shè)置版本信息

gradle就是groovy,所以可以加入一些groovy代碼攒菠,如下:

  // 版本
  class ProjectVersion {
    int major
    int minor
    int min

    boolean release

    ProjectVersion(int major, int minor, int min) {
        this.major = major
        this.minor = minor
        this.min = min
        this.release = false
    }

    ProjectVersion(int major, int minor, int min, boolean release) {
        this.major = major
        this.minor = minor
        this.min = min
        this.release = release
    }


    @Override
    public String toString() {
        return "$major.$minor.$min${release ? '' : '-SNAPSHOT'}"
    }
}

def version = new ProjectVersion(1, 0, 0)
task printVersion << {
    println(" >>>>>> the verson is ${version}")
}

添加task配置塊

task配置塊會(huì)在task動(dòng)作之前被執(zhí)行迫皱;

  1. 新建配置文件version.properties如下:
major=1
minor=0
min=0
release=false
  1. 添加Gradle配置 loadVersion(沒(méi)有 <<>> 運(yùn)算符) ,并讀取 properties文件
ext.versionFile = file('version.properties')
// loadVersion任務(wù)辖众,沒(méi)有 << or >> 運(yùn)算符卓起,Gradle稱之為 task配置
task loadVersion {
    project.version = readVersion()
}

ProjectVersion readVersion() {
    println(">>>>> reading the version file")
    if(!versionFile.exists()) {
        throw new RuntimeException(">>> Required version file does not exist: ${versionFile.canonicalPath}")
    }

    Properties prop = new Properties()
    versionFile.withInputStream { stream ->
        prop.load(stream)
    }

    new ProjectVersion(prop.major.toInteger(), prop.minor.toInteger(), prop.min.toInteger(), prop.release.toBoolean())
}

task printVersion << {
    println(" >>>>>> the verson is ${version}")     // 訪問(wèn)project的屬性version
}

Gradle構(gòu)建生命周期階段
執(zhí)行g(shù)radle構(gòu)建,會(huì)運(yùn)行三個(gè)不同的生命周期階段:初始化凹炸、配置和執(zhí)行戏阅;
如下圖:

Gradle構(gòu)建生命周期的構(gòu)建階段的順序
  1. 初始化階段:gradle為項(xiàng)目創(chuàng)建一個(gè)Project實(shí)例;
  2. 配置階段:Gradle構(gòu)造一個(gè)模型來(lái)表示任務(wù)啤它,此階段用來(lái)為項(xiàng)目或指定的task設(shè)置所需的配置奕筐;項(xiàng)目的每一次構(gòu)建的任何配置代碼都可以被執(zhí)行,即使只執(zhí)行 gradle tasks;
  3. 執(zhí)行階段:執(zhí)行task動(dòng)作蚕键;執(zhí)行的順序由他們的依賴決定救欧;如果任務(wù)被認(rèn)為沒(méi)有修改過(guò),將跳過(guò)锣光;

聲明task的inputs和outputs

Gradle通過(guò)比較2個(gè)構(gòu)建task的inputs和outputs來(lái)決定是否是最新的;當(dāng) inputs和outputs不同時(shí)铝耻,task才運(yùn)行誊爹,否則跳過(guò);
輸入可以是一個(gè)目錄瓢捉,文件或?qū)傩缘绕登穑粋€(gè)task的輸出是通過(guò)一個(gè)目錄或1~n個(gè)文件來(lái)定義的;
inputs與outputs在DefaultTask類中泡态,被定義為屬性或者直接子類來(lái)表示搂漠;

DefaultTask類定義的task的inputs和outputs

創(chuàng)建Task用來(lái)發(fā)布產(chǎn)品版本,并自動(dòng)修改配置文件

// 產(chǎn)品發(fā)布版本
task makeReleaseVersion(group: 'versioning', description: 'Makes project a reelase version.') << {
    version.release = true
    // ant task 的propertyfile 提供便利的方式修改屬性文件 (version.properties)
    ant.propertyfile(file: versionFile) {
        entry(key: 'release', type: 'string', operation: '=', value: 'true')
    }
}

通過(guò)運(yùn)行 :gralde makeReleaseVersion 可以發(fā)現(xiàn)配置文件某弦,確實(shí)修改了桐汤;

但Gradle并不知道 我們的 是發(fā)布版本而克,為了解決,需要聲明它的inputs與outputs

task makeReleaseVersion(group: 'versioning', description: 'Makes project a reelase version.') {
    // 在配置階段聲明 inputs/outputs
    inputs.property('release', version.release)   // 聲明版本的release屬性作為輸入
    outputs.file versionFile                      // 由于版本文件被修改怔毛,所以他被聲明最為輸出文件屬性

    doLast {    // 閉包為動(dòng)作代碼
        version.release = true
        ant.propertyfile(file: versionFile) {
            entry(key: 'release', type: 'string', operation: '=', value: 'true')
        }
        println(">>>>>> makeReleaseVersion")
    }
}

task的inputs和outputs是在配置階段執(zhí)行的用來(lái)連接task依賴员萍,需要在配置塊中被定義,而且需要確保賦給 inputs和outputs的值在配置階段是可訪問(wèn)的拣度;
如果需要編程獲得輸出碎绎,可通過(guò) TaskOutputs上的upToDateWhen(Closure)方法實(shí)現(xiàn),此方法是在執(zhí)行階段執(zhí)行的抗果;如果閉包返回true筋帖,則認(rèn)為該task時(shí)最新的;

如果2次執(zhí)行 gradle makeReleaseVersion 后一次gradle知道項(xiàng)目版本被設(shè)置為發(fā)布版本了冤馏,并自動(dòng)跳過(guò)第二次執(zhí)行日麸;

UP-TO-DATE

使用自定義Task

自定義Task包含2個(gè)組件:自定義的task類(封裝邏輯行為)與真實(shí)的task(提供用于配置行為的task類所暴露的屬性值);gradle稱之為增強(qiáng)型task宿接;
可維護(hù)性是編寫(xiě)自定義task類的優(yōu)勢(shì)之一赘淮,畢竟操作的是一個(gè)實(shí)際的類;

編寫(xiě)自定義Task

繼承自 DefaultTask睦霎,使用注解表示輸入輸出梢卸;

class ReleaseVersionTask extends DefaultTask {

    @Input Boolean release      // 注解聲明輸入屬性 release
    @OutputFile File destFile   // 定義輸出文件

    ReleaseVersionTask() {
        group = 'versioning'
        description = 'Makes project a realase version.'
    }

 // 使用注解聲明將被執(zhí)行的方法 ,
 // 加上這個(gè)action的作用是當(dāng)執(zhí)行這個(gè)task的時(shí)候會(huì)自動(dòng)執(zhí)行這個(gè)方法
    @TaskAction    
    void start() {
        project.version.release = true
        ant.propertyfile(file: versionFile) {
            entry(key: 'release', type: 'string', operation: '=', value: 'true')
        }
    }
}

使用自定義Task
在構(gòu)建腳本中副女,創(chuàng)建一個(gè)ReleaseVersionTask類型的task蛤高,并且通過(guò)它的屬性賦值來(lái)設(shè)置輸入和輸出,如:

// 增強(qiáng)型task暴露的屬性單獨(dú)賦值
task makeReleaseVersion2(type: ReleaseVersionTask) {
    release = version.relase
    destFile = versionFile
}

task規(guī)則

某些時(shí)候碑幅,編寫(xiě)的task可能做著相同的事情戴陡,如下版本的管理task任務(wù):

// 主版本+1
task incrementMajorVersion(group: 'versioning', description: 'Increments project major version.') << {
    String currentVersion = version.toString()
    ++version.major
    String newVersion = version.toString()
    logger.info "Incrementing major project version: $currentVersion -> $newVersion"

    ant.propertyfile(file: versionFile) {
        entry(key: 'major', type: 'int', operation: '+', value: 1)
    }
}

// 次版本+1
task incrementMinorVersion(group: 'versioning', description: 'Increments project major version.') << {
    String currentVersion = version.toString()
    ++version.minor
    String newVersion = version.toString()
    logger.info "Incrementing minor project version: $currentVersion -> $newVersion"

    ant.propertyfile(file: versionFile) {
        entry(key: 'minor', type: 'int', operation: '+', value: 1)
    }
}

分別運(yùn)行命令:gradle incrementMinorVersion -i ,可以看到輸出沟涨;
但上面的是否可以改進(jìn)呢恤批?

task規(guī)則的命名模式

根據(jù)task名稱模式執(zhí)行特定的邏輯,模式由2部分組成:
task名稱的靜態(tài)部分 與 一個(gè)占位符裹赴,他們聯(lián)合起來(lái)組成一個(gè)命令喜庞;
如上面的,像:increment<Classifier>Version

Gralde的一些核心插件棋返,利用了task規(guī)則延都,如:clean<TaskName>;

聲明task規(guī)則

首先需要獲得對(duì)TaskContainer的引用,然后可調(diào)用其addRule(String, Closure) 方法睛竣,通過(guò)project的getTasks()方法獲取 TaskContainer引用晰房;
使用task規(guī)則,實(shí)現(xiàn)上線動(dòng)作:

// 使用 taskContainer 添加規(guī)則
tasks.addRule("Pattern: increment<Classifier>Version - Increments the project version.") {
    String taskName ->
        if (taskName.startsWith('increment') && taskName.endsWith('Version')) {
            task(taskName) << {  // 符合命名模式的task動(dòng)態(tài)添加doLast方法
                String classifier = (taskName - 'increment' - 'Version'.toLowerCase())  // 提取
                String currentVersion = version.toString()

                switch (classifier) {
                    case 'major': ++version.major
                        break
                    case 'minor': ++version.minor
                        break
                    default: throw new GradleException("Invalid version type '$classifier', Allowed " +
                            " types: ['Major','Minor']")
                }

                String newVersion = version.toString()
                logger.info "Incrementing $classifier project version: $currentVersion -> $newVersion"
                ant.propertyfile(file: versionFile) {
                    entry(key: classifier, type: 'int', operation: '+', value: 1)
                }
            }
        }
}

通過(guò)運(yùn)行gradle tasks會(huì)列出一個(gè)具體的tasks組rules:

tasks Rules 組

task 規(guī)則不能分組,其顯示在 Rules組下殊者;

我們運(yùn)行命令:gradle incrementMajorVersion -i与境,輸出如下:

執(zhí)行task的分組任務(wù)的輸出

在buildSrc目錄下構(gòu)建代碼

可以將構(gòu)建腳本的一些groovy類:如上面的ProjectVersion和自定義的task ReleaseVersionTask,這些適合移動(dòng)到項(xiàng)目的 buildSrc目錄下幽污;
Gradle在buildSrc目錄下使源文件結(jié)構(gòu)標(biāo)準(zhǔn)化嚷辅,Java代碼在 src/main/java,Groovy代碼放在src/main/groovy目錄下;這些目錄下代碼會(huì)自動(dòng)編譯距误,并添加到Gradle構(gòu)建腳本的classpath中簸搞;

Android Studio 中在項(xiàng)目的根目錄,建立buildSrc文件夾准潭,并sync一下工程趁俊,然后新增文件夾路徑 src/main/groovy/包名, 就可以創(chuàng)建groovy腳本了;

buildSrc項(xiàng)目出來(lái)了

在當(dāng)前 module下對(duì)應(yīng)的 build.gradle文件刑然,直接引入 buildSrc 下的類寺擂,這樣Task定義與build.gradle分離了;

import com.better.ProjectVersion2
import com.better.ReleaseVersionTask2

總結(jié):

了解一下Task的工作細(xì)節(jié)泼掠、自定義Task怔软,也了解一些Task配置與Task動(dòng)作之間的區(qū)別;最后我們?cè)赽uildSrc中择镇,實(shí)現(xiàn)自定義Task的分離挡逼,將task獨(dú)立出來(lái);

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末腻豌,一起剝皮案震驚了整個(gè)濱河市家坎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌吝梅,老刑警劉巖虱疏,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異苏携,居然都是意外死亡做瞪,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門右冻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)穿扳,“玉大人,你說(shuō)我怎么就攤上這事国旷。” “怎么了茫死?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵跪但,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)屡久,這世上最難降的妖魔是什么忆首? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮被环,結(jié)果婚禮上糙及,老公的妹妹穿的比我還像新娘。我一直安慰自己筛欢,他們只是感情好浸锨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著版姑,像睡著了一般柱搜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上剥险,一...
    開(kāi)封第一講書(shū)人閱讀 51,146評(píng)論 1 297
  • 那天聪蘸,我揣著相機(jī)與錄音,去河邊找鬼表制。 笑死健爬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的么介。 我是一名探鬼主播纫事,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼瀑粥!你這毒婦竟也來(lái)了恒序?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤鸽扁,失蹤者是張志新(化名)和其女友劉穎蒜绽,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體桶现,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡躲雅,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了骡和。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片相赁。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖慰于,靈堂內(nèi)的尸體忽然破棺而出钮科,到底是詐尸還是另有隱情,我是刑警寧澤婆赠,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布绵脯,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蛆挫。R本人自食惡果不足惜赃承,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望悴侵。 院中可真熱鬧瞧剖,春花似錦、人聲如沸可免。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)巴元。三九已至毡咏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間逮刨,已是汗流浹背呕缭。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留修己,地道東北人恢总。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像睬愤,于是被迫代替她去往敵國(guó)和親片仿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

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

  • 這篇文章講給大家?guī)?lái)gradle打包系列中的高級(jí)用法-自己動(dòng)手編寫(xiě)gradle插件尤辱。我們平常在做安卓開(kāi)發(fā)時(shí)砂豌,都會(huì)在...
    呆萌狗和求疵喵閱讀 15,984評(píng)論 22 80
  • 本篇主要是個(gè)人學(xué)習(xí)gradle的筆記總結(jié) 一.開(kāi)始之前 1. 為什么學(xué)習(xí)Gradle 采用DSL(Doma...
    zyq_neuq閱讀 1,503評(píng)論 2 12
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)光督,斷路器阳距,智...
    卡卡羅2017閱讀 134,651評(píng)論 18 139
  • 前一段時(shí)間看到不少人在技術(shù)論壇里問(wèn)「剛學(xué) Android 不久,對(duì) Gradle 不懂结借,看了很多資料依然一知半解」...
    f9dd77add98e閱讀 3,514評(píng)論 1 8
  • 前言 為什么需要學(xué)Gradle? Gradle 是 Android 現(xiàn)在主流的編譯工具筐摘,雖然在Gradle 出現(xiàn)之...
    真笨笨魚(yú)閱讀 1,490評(píng)論 0 0