七破加、Gradle中的Project

  • 說在前面:Gradle中project是非常重要的俱恶,所以也會有非常多的API及其可配置的屬性,筆者也有許多不了解的范舀,在這只是講一些開發(fā)中比較常用的一些API和屬性合是。但是了解了這些,其它的自己去看锭环,去查資料相信也是可以搞懂的聪全。當(dāng)遇到了一些需求是以下API或者屬性配置不能解決的,可以到Project文檔中查查有沒有可以幫助到自己的API田藐。其它想了解的在這也可以查閱荔烧。另外,Android Plugin DSL Reference中可查閱android閉包中有哪些可配置選項(xiàng)汽久。共勉

一鹤竭、Project概念

  1. 什么是Project
  • 前面說過,每個(gè)module都會對應(yīng)著一個(gè)Project景醇。先將項(xiàng)目切換到project面板

    project面板.png
  • 我們也知道臀稚,初始化階段,會從settings.gradle中解析生成Project

    settings.gradle.png
  • 可以看到settings.gradle中include了app三痰、annottations(小手一抖多了個(gè)t,注意一下就好annotations)吧寺、compiler窜管、core、ec,那么我們的項(xiàng)目中有幾個(gè)Project對象呢稚机?是5個(gè)嗎幕帆?下面我們執(zhí)行projects這個(gè)Task看一下我們的項(xiàng)目中有幾個(gè)Project

    執(zhí)行projects task.png
  • 很明顯,有個(gè)根project 'ecommoerce'赖条,在它的下面有5個(gè)子Project,所以一共有6個(gè)Project失乾。Gradle以樹的形式管理Project,最外層有一個(gè)根Project,在它下面有其它的幾個(gè)子Proect。Gradle是根據(jù)目錄中有沒有build.gradle文件來判斷這個(gè)目錄是不是一個(gè)Project,我們可以看到在根目錄中有一個(gè)build.gradle文件纬乍,在每個(gè)子module中也有它們各自的build.gradle文件碱茁。而每個(gè)Project都是在build.gradle中去配置和管理的,這些build.gradle最終會被Gradle編譯為Project字節(jié)碼仿贬。
  1. Project的作用
    根Project是用來統(tǒng)籌管理所有的子Project的纽竣,而每個(gè)子Project都對應(yīng)了一個(gè)輸出。比如我們的app module的類型是application的茧泪,那么它最終就對應(yīng)生成了一個(gè)APK文件,是android library類型的蜓氨,最終會生成一個(gè)aar文件,java library類型的生成一個(gè)jar文件等等

二队伟、Project核心API

  • Gradle生命周期API

    在Gradle生命周期及其監(jiān)聽中其實(shí)已經(jīng)用過了语盈,里面的this其實(shí)就是project,直接用project替換this也是可以的缰泡。這一部分的API是用來監(jiān)聽Gradle的生命周期的。

  • project相關(guān)API

    這一部分主要是在每個(gè)project中獲取自己父Project和子Project的代嗤,為每個(gè)Project提供操作父Project和管理子Project的能力

  1. 獲取當(dāng)前project及其子project


    獲取project所有的child.png
  • 注意上面的方法是寫在根Project的build.gradle中的棘钞,若是在子module中的build.gradle編寫,則無法得到下面的結(jié)果(模仿執(zhí)行projects Task的結(jié)果)干毅,可能只輸出了一個(gè)“Root project:[所在的module ]”,因?yàn)閜roject的getAllProject方法是將當(dāng)前project當(dāng)成root project宜猜,并且獲取當(dāng)前project的所有子project。

    獲取child結(jié)果.png
  1. 獲取根project


    獲取項(xiàng)目的根project.png
  • 注意它們之間的區(qū)別

    執(zhí)行結(jié)果.png
  1. 獲取project的子project
//調(diào)用自己定義的方法硝逢,方法中調(diào)用了gradle提供的API getSubprojects()
this.getSubProjects()

def getSubProjects() {
    //this.rootProject 獲取項(xiàng)目的根目錄姨拥,
    // getSubprojects()方法返回所有子Project的Set集合,遍歷
    def subProjectSet = this.rootProject.getSubprojects()
    subProjectSet.eachWithIndex {
        Project project, int index ->
            if (index > 0 && index < projectSet.size() - 1) {
                println "+--- Project ':$project.name'"
            } else {
                println "\\--- Project ':$project.name'"
            }
    }
}

//在終端執(zhí)行g(shù)radlew clean渠鸽,以下是結(jié)果
'''
> Configure project :app
\--- Project ':annottations'
+--- Project ':app'
+--- Project ':compiler'
+--- Project ':core'
\--- Project ':ec'

'''

  • 順帶說一下:因?yàn)閳?zhí)行clean Task叫乌,會走Gradle的生命周期,而配置階段是需要解析所有Project的所有Task,生成拓?fù)鋱D徽缚,前面也有提到憨奸,build.gradle最終會被Gradle編譯為Project字節(jié)碼,所以我們在build.gradle中編寫我們的腳本凿试,實(shí)際上就是在Project內(nèi)部編寫排宰。也就是說我們在app module的build.gradle調(diào)用編寫的方法的腳本似芝,會在解析的時(shí)候被調(diào)用,所以即使是我們跑Task clean板甘,也會輸出我們想要的結(jié)果党瓮。

4.獲取父project

//在 app build.gradle中加入以下

this.getParentProject()
def getParentProject() {
    def name = "parent為null"
    def parent = this.getParent()
    if (parent != null) { //根Project,已經(jīng)是最頂層,沒有parent
        name = parent.name
    }
    println "project ${this.name} 的parent為:$name"
}


//在終端執(zhí)行g(shù)radlew clean盐类,以下是輸出結(jié)果
'''
> Configure project :app
project app 的parent為:ecommerce

'''

  • Gradle以樹的形式管理Project寞奸,通過前面的幾個(gè)API,已經(jīng)將所有的Project都關(guān)聯(lián)起來了,我們可以做到在任意的build.gradle(project)中獲取其它的project傲醉。另外在Gradle中根project是用來統(tǒng)籌管理所有子project的蝇闭,所以Gradle提供了更加方便的API去管理其子project
  1. project方法配置project
// Project project(String path, Closure configureClosure);

//在根project中的build.gradle
project('app') { Project project ->
    //doSomething for app project
    //比如為app 工程強(qiáng)制使用某個(gè)版本的依賴來解決依賴沖突中出現(xiàn)的依賴
    project.configurations.all {
        resolutionStrategy {
            force 'com.android.support:support-annotations:26.1.0'
        }
    }

    //指定輸出
    apply plugin:'com.android.application'
    //添加group
    group 'com.github'
    //指定版本
    version '1.0.0'
    //凡是project中可以配置的都可以進(jìn)行配置
    //比如添加依賴
    dependencies{

    }
    //添加android相關(guān)配置
    android{

    }
}
    
    //同樣的還可以為其它的project配置,這里就不再配置了硬毕,和上面是一樣的呻引,將‘a(chǎn)pp’替換成其它module的名字,然后及對該module進(jìn)行配置

  • 當(dāng)然每個(gè)Project自己所特有的吐咳,最好還是在它自己的build.gradle中配置逻悠,Gradle為每個(gè)module都提供了自己的build.gradle(project),正是為了讓它們職責(zé)分明,保證單一原則韭脊,你在你那配置你自己的東西就好了童谒。而在根project管理子project,一般都是給這些子project配置一些公共的東西沪羔,但是project方法饥伊,還是需要一個(gè)一個(gè)的配置,比如我們每個(gè)module的group蔫饰、version等都是相同的琅豆,或者說都依賴某個(gè)公共的module,每個(gè)build.gradle都需要重復(fù)配置篓吁,肯定是不符合編碼規(guī)則的茫因,就像我們編碼時(shí),重復(fù)的代碼杖剪,我們都會想辦法將它們提取出來冻押。Gradle也提供了這樣的方法。

  • 統(tǒng)一配置

//在根project中的build.gradle
//allprojects 為當(dāng)前project及其所有子project配置
allprojects {
    //倉庫
    repositories {
        google()
        jcenter()
    }

    //添加group
    group 'com.github'
    //指定版本
    version '1.0.0'
}

//打印從未配置過的compiler module的group看看是否配置成功
println project('compiler').group

//以下是輸出結(jié)果盛嘿,因?yàn)楣P記添加圖片太麻煩了洛巢,所以直接拷貝終端的輸出結(jié)果
'''
C:\Users\***\Desktop\project\ecommerce>gradlew clean
Starting a Gradle Daemon, 2 incompatible Daemons could not be reused, use --status for details

> Configure project :
com.github


BUILD SUCCESSFUL in 16s
6 actionable tasks: 4 executed, 2 up-to-date
'''

  • 統(tǒng)一配置,不包括當(dāng)前project
//比如我們上傳我們的library module到Maven倉庫次兆,我們的root project一般是不需要上傳上去的狼渊,需要將子module上傳上去,就可以寫一個(gè)上傳的gradle文件,然后為所有的子module引入狈邑。

//省略了project參數(shù)
subprojects {
    //庫工程才需要上傳到Maven
    if (project.plugins.hasPlugin("com.android.library")){
        //當(dāng)一個(gè)功能比較獨(dú)立時(shí)城须,可以寫成一個(gè)單獨(dú)的.gradle文件,然后再需要的地方apply from:'gradle文件path'米苹,即可使用該功能
        apply from:'../repositories_upload.gradle'
    }
}

  • 以下是上傳到Maven的repositories_upload.gradle文件糕伐。為了便于理解,下面的是上傳一個(gè)library蘸嘶,因?yàn)槔锩孢€涉及到Task良瞧、屬性,引入外部文件等方面的內(nèi)容训唱,并且一些Maven的用戶名密碼褥蚯、倉庫url等都是定義在其它的地方,所以可能看起來有點(diǎn)混亂况增,但是講完之后再來看就可以看的明白了赞庶。結(jié)合注釋,絕大部分都是能夠理解的澳骤。我們學(xué)習(xí)Gradle本身就是需要使用它滿足我們開發(fā)中各種需求歧强,所以實(shí)際的應(yīng)用能讓我們更加的了解如何使用Gradle構(gòu)建項(xiàng)目并完成各種需求。
apply plugin: 'maven' //maven倉庫上傳插件
apply plugin: 'maven-publish' //maven倉庫上傳插件
configurations {
    deployerJars
}

repositories {
    mavenCentral()
}

// 判斷版本是Release or Snapshots
def isReleaseBuild() {
    return !VERSION.contains("SNAPSHOT")
}

// 獲取倉庫url
def getRepositoryUrl() {
//    return isReleaseBuild() ? RELEASE_URL : SNAPSHOT_URL
    return Repository_Url
}

//配置上傳信息为肮,最重要的是這一段
//我們平時(shí)添加依賴的時(shí)候就是這樣compile 'com.android.support:support-annotations:26.1.0'
//com.android.support 對應(yīng) GROUP_ID
//support-annotations 對應(yīng) ARTIFACT_ID
//26.1.0 對應(yīng) VERSION
//而getRepositoryUrl()獲取的是你的Maven倉庫URL,userName和password分別對應(yīng)Maven倉庫的用戶名和密碼摊册,上傳到Maven倉庫之前需要有你自己Maven倉庫,這一步大家google一下基本都會了颊艳,當(dāng)然也可以發(fā)布到本地倉庫
uploadArchives {
    repositories {
        mavenDeployer {
            pom.version = VERSION
            pom.groupId = GROUP_ID
            pom.artifactId = ARTIFACT_ID
            pom.packaging ='aar'
            repository(url: getRepositoryUrl()) {
                authentication(userName: Authentication_UserName, password: Authentication_Password)
            }
        }
    }
}

// type顯示指定任務(wù)類型或任務(wù), 這里指定要執(zhí)行Javadoc這個(gè)task,這個(gè)task在gradle中已經(jīng)定義
task androidJavadocs(type: Javadoc) {
    // 設(shè)置源碼所在的位置
    source = android.sourceSets.main.java.sourceFiles
}

// 生成javadoc.jar
task androidJavadocsJar(type: Jar) {
    // 指定文檔名稱
    classifier = 'javadoc'
    from androidJavadocs.destinationDir
}

// 生成sources.jar
task androidSourcesJar(type: Jar) {
    classifier = 'sources'
    from android.sourceSets.main.java.sourceFiles
}

// 產(chǎn)生相關(guān)配置文件的任務(wù)
artifacts {
    archives androidSourcesJar
    archives androidJavadocsJar
}
  • 題外話:學(xué)習(xí)更多優(yōu)秀的框架中的是如何使用Gradle的能幫助我們更好的學(xué)習(xí)茅特。因?yàn)槟切┦钦嬲趯?shí)際應(yīng)用中使用過的東西。并且經(jīng)過重重檢驗(yàn)棋枕。比如熱修復(fù)框架Tinker的build.gradle∥轮危現(xiàn)在看起來可能還不太能看的懂,但是也能看懂很多的東西了戒悠,只是可能里面一些屬性的配置不太了解,結(jié)合Tinker接入指南舟山,因?yàn)槭菍W(xué)習(xí)Gradle绸狐,所以看看里面的一些屬性大概是什么意思即可,也不需要深入了解累盗。建議學(xué)完之后再去看看寒矿,畢竟學(xué)習(xí)Gradle,如果能夠看懂并且學(xué)習(xí)微信在Tinker中是如何使用Gradle的若债,那么對于我們Gradle的學(xué)習(xí)肯定是很有幫助的.

  • 屬性相關(guān)API
    Gradle本身為Project提供一些屬性符相,屬性相關(guān)API可以讓我們使用這些屬性,并且讓我們可以為Project添加額外的屬性

  1. Project自帶屬性
    /**
     * The default project build file name.
     * 所以所有的Project都需要一個(gè)默認(rèn)的build.gradle文件,Gradle默認(rèn)從該文件讀取配置信息
     */
    String DEFAULT_BUILD_FILE = "build.gradle";

    /**
     * The hierarchy separator for project and task path names.
     *路徑分割符啊终,如windows文件路徑使用斜杠分割
     */
    String PATH_SEPARATOR = ":";

    /**
     * The default build directory name.
     * 默認(rèn)的build輸出文件夾镜豹,默認(rèn)build產(chǎn)生的apk等產(chǎn)物在此目錄
     */
    String DEFAULT_BUILD_DIR_NAME = "build";

    /**
     *  Project-wide Gradle settings.
     *  IDE (e.g. Android Studio) users:
     *  Gradle settings configured through the IDE *will override*
     *  any settings specified in this file.
     *  在此屬性文件中可修改一些Gradle默認(rèn)的屬性,也可擴(kuò)展屬性
     */
    String GRADLE_PROPERTIES = "gradle.properties";

    /**
     * 以下三個(gè)屬性基本上不會使用到
     */
    String SYSTEM_PROP_PREFIX = "systemProp";

    String DEFAULT_VERSION = "unspecified";

    String DEFAULT_STATUS = "release";
  1. 為Gradle擴(kuò)展屬性
    Gradle自帶的默認(rèn)屬性肯定是無法滿足開發(fā)需求的,我們可以為Gradle擴(kuò)展各種各樣的屬性來管理整個(gè)項(xiàng)目
//先來看build.gradle中常見的一部分配置蓝牲,此處只做演示刪掉大部分部分配置

apply plugin: 'com.android.application'
android {
    compileSdkVersion 26
}
dependencies {
    implementation 'com.android.support:appcompat-v7:26.1.0'
}

//前面說過build.gradle在配置階段會被解析成Project字節(jié)碼趟脂,所以在這里的配置實(shí)際上是在Project類中編寫代碼,而不僅僅是配置例衍。
//寫代碼的話昔期,里面的一些寫死的魔法值如編譯sdk版本26,還有寫死的字符串a(chǎn)ppcompat-v7依賴com.android.support:appcompat-v7:26.1.0都是不符合編碼風(fēng)格的
//尤其是許多依賴的版本號都是一樣的佛玄,如各種support包的版本我們可能使用一樣的硼一,寫死我們修改就需要修改多個(gè)地方,或者說多個(gè)module中都需要使用的屬性梦抢,在每個(gè)module中寫死更是如此
//所以我們可以像平時(shí)開發(fā)一樣般贼,定義一些屬性,并在配置的時(shí)候使用定義好的屬性
//直接定義屬性
apply plugin: 'com.android.application'

def mCompileSdkVersion = 26
def libSupportV7 = 'com.android.support:appcompat-v7:26.1.0'
android {
    compileSdkVersion mCompileSdkVersion
}
dependencies {
   implementation libSupportV7
}
//使用ext閉包擴(kuò)展屬性
apply plugin: 'com.android.application'
ext{
   compileSdkVersion = 26
   libSupportV7 = 'com.android.support:appcompat-v7:26.1.0' 
}
android {
    compileSdkVersion this.compileSdkVersion
}
dependencies {
   implementation this.libSupportV7
}

//直接定義屬性和使用ext閉包擴(kuò)展屬性沒有本質(zhì)的區(qū)別

//但當(dāng)定義的屬性是所有的module都需要使用的時(shí)候,使用ext的優(yōu)勢就可以提現(xiàn)出來了,我們可以在根工程中使用ext擴(kuò)展這些屬性膳汪,然后在子工程中利用前面提到的rootProject來使用這些屬性
//定義在根工程
ext{
   compileSdkVersion = 26
   libSupportV7 = 'com.android.support:appcompat-v7:26.1.0' 
}

//在子工程中使用
apply plugin: 'com.android.application'
android {
    compileSdkVersion this.rootProject.compileSdkVersion
}
dependencies {
   implementation this.rootProject.libSupportV7
}

//注意此處也可以省略掉rootProject宙刘,直接像上面使用this調(diào)用,因?yàn)樵贕radle中裆熙,根工程定義的屬性會被子工程繼承
//一個(gè)項(xiàng)目可能有很多module,需要有很多的依賴和其它配置,所有的屬性都寫在根工程中可能導(dǎo)致根工程的build.gradle文件內(nèi)容異常的多,不容易查找
//可以像上傳library到Maven一樣萤厅,將這些屬性都定義到一個(gè)單獨(dú)的文件中,然后在根工程中引入即可靴迫。

//根目錄中新建commom_properties.gradle文件(定義兩個(gè)map)
ext{
    android     =  [compileSdkVersion:26,
                    buildToolsVersion:'26.0.2']
               
    dependencies = [libSupportV7:'com.android.support:appcompat-v7:26.1.0' ,
                   libConstraintLayout:'com.android.support.constraint:constraint-layout:1.1.3']
}

//在根工程中引入
apply from:this.file('commom_properties.gradle') //file方法從當(dāng)前project目錄中查找文件

//在子工程中使用(ext中定義了各種map惕味,直接使用key訪問值)
apply plugin: 'com.android.application'
android {
    compileSdkVersion rootProject.ext.android.compileSdkVersion
    buildToolsVersion rootProject.ext.android.buildToolsVersion
}
dependencies {
   implementation rootProject.ext.dependencies.libSupportV7
   implementation rootProject.ext.dependencies.libConstraintLayout
}
//在gradle.properties文件中修改屬性
//Android Studio 3.0會在debug apk的manifest文件application標(biāo)簽里自動添加
//android:testOnly="true"屬性,導(dǎo)致IDE中run跑出的apk在大部分手機(jī)上只能用
//adb install -t <apk>來安裝玉锌,在oppo手機(jī)上甚至安裝不了
//解決辦法:在gradle.properties文件中修改該屬性

android.injected.testOnly=false //修改testOnly屬性為false


//添加屬性名挥,gradle.properties除了可以修改已有屬性之外,還可添加屬性主守,全局可用
//在gradle.properties文件中添加
mApplicationId = wen.github.ecommerce
mVersionCode = 1
mCompileSdkVersion = 26

//在工程中使用
apply plugin: 'com.android.application'
android {
   compileSdkVersion mCompileSdkVersion
   defaultConfig{
       applicationId mApplicationId
       versionCode mVersionCode.toInteger()
   }
}

  • 對于gradle屬性文件gradle.properties禀倔,想要了解更多點(diǎn)擊構(gòu)建環(huán)境
  • 另外一個(gè)local.properties,用于構(gòu)建系統(tǒng)配置本地環(huán)境屬性参淫,例如SDK安裝路徑救湖。由于該文件的內(nèi)容由AS自動生成并且專用于本地開發(fā)者環(huán)境,因此不應(yīng)手動修改該文件涎才,或?qū)⑵浼{入版本控制系統(tǒng)鞋既。
  • File相關(guān)API
    用于當(dāng)前Project下一些文件的處理
  1. 路徑獲取相關(guān)API
//獲取根工程的路徑
println getRootDir().absolutePath
//獲取build目錄路徑
println getBuildDir().absolutePath
//獲取當(dāng)前工程路徑
println getProjectDir().absolutePath
獲取文件路徑.png
  1. 文件操作相關(guān)API
//查找文件

//在根工程中
println getFileContent('gradle.properties') //輸出gradle.properties的內(nèi)容

def getFileContent(String path) {
    try {
        def file = file(path)
        return file.text
    } catch (GradleException e) { //file()方法在當(dāng)前project目錄下找,找不到會報(bào)FileNotFound異常
        println "File $path not found"
    }
    return null
}

//files()方法,接收多個(gè)路徑參數(shù)邑闺,基于當(dāng)前project工程查找多個(gè)文件跌前,返回一個(gè)collection,使用與file()方法一致
gradle.properties內(nèi)容.png
//文件的拷貝,講解Groovy語法之文件操作中通過讀取文件中的內(nèi)容检吆,然后寫入到目標(biāo)文件實(shí)現(xiàn)
//Gradle提供了更加簡便的方法:copy()

//app module的build.gradle文件中
copy {
    from file('build/outputs/') //可以是文件也可以是目錄
    into getRootProject().getBuildDir().path + '/rootOutputs/' //目標(biāo)路徑
    include '**/*.apk'   //選擇要復(fù)制的文件
    include '**/*.json' //選擇要復(fù)制的文件
    exclude { detail -> detail.file.name.contains('json') } //排除
    rename { 'aaa.apk'} //重命名
}

//將生成的outputs文件目錄整個(gè)拷貝到根工程的build目錄下的rootOutputs文件夾中
  • 源路徑文件

    源路徑文件.png
  • 目標(biāo)路徑文件

    目標(biāo)路徑文件.png
//文件樹的遍歷
fileTree("src/main/java") {//基于基準(zhǔn)目錄創(chuàng)建文件樹
    FileTree fileTree ->
        fileTree.visit {//訪問文件樹的元素
            FileTreeElement element ->
                println element.file.name
        }
}


//輸出結(jié)果
'''
wen
github
ecommerce
ECApplication.java
MainActivity.java

'''

  • 關(guān)于文件操作的的更多細(xì)節(jié)操作可以參考官網(wǎng)的 User Guide中文件操作部分舒萎,極客學(xué)院的Gradle 用戶指南官方文檔中文版

  • 依賴相關(guān)API
    project添加依賴蹭沛,引入外部的文件等等

//Android項(xiàng)目的根工程的build.gradle中的buildscript塊相信大家都見過臂寝,新建的項(xiàng)目的時(shí)候會自動為我們配置一些東西

//根工程的build.gradle中
buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'
        
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

//為什么這樣寫不知道大家有沒有考慮過,里面又能配置些什么
//buildscript塊實(shí)際上就是Project.buildscript()方法相信大家都可以理解摊灭,點(diǎn)進(jìn)方法的源碼可以看到這個(gè)閉包接收一個(gè)ScriptHandler類型的參數(shù)咆贬,那么能夠配置的東西就是ScriptHandler類里面提供的給我們的,主要有兩個(gè)配置方法repositories和dependencies帚呼。
//這個(gè)buildscript塊寫完整實(shí)際上是下面這樣掏缎,至于為什么可以去掉scriptHandler是因?yàn)殚]包中的delegate就是去掉的scriptHandler,在閉包中只要this,owner,delegate中有的方法都是可以調(diào)用的煤杀,區(qū)別是調(diào)用的順序根據(jù)閉包委托策略決定(可以到Groovy語法中講解閉包的this,owner,delegate部分了解)眷蜈。

buildscript{ ScriptHandler scriptHandler ->
    //配置工程的倉庫地址
    scriptHandler.repositories {}
    
    //配置工程的“插件”依賴地址
    scriptHandler.dependencies {}
}

//同樣的點(diǎn)進(jìn)repositories和dependencies方法的源碼可查看這兩個(gè)方法接收的參數(shù)以及可配置的內(nèi)容

buildscript { ScriptHandler scriptHandler ->
    //配置工程的倉庫地址
    scriptHandler.repositories { RepositoryHandler repositoryHandler ->
        //一個(gè)項(xiàng)目可以有好幾個(gè)庫. Gradle 會根據(jù)依賴定義的順序在各個(gè)庫里尋找它們。在第一個(gè)庫里找到了就不會再在第二個(gè)庫里找它了
        //所以下面都是可選的沈自,我們項(xiàng)目添加的那些依賴庫存放在哪個(gè)倉庫就需要在這配置酌儒,Gradle才能幫我們找到這些依賴庫并下載下來
        repositoryHandler.google()  //google倉庫
        repositoryHandler.jcenter()  //jcenter庫
        repositoryHandler.mavenCentral() //maven中心庫
        repositoryHandler.mavenLocal() //本地maven庫
        repositoryHandler.maven { url "https://maven.google.com" } //配置maven私有倉庫
        repositoryHandler.maven { url "https://jitpack.io" } //可以寫多個(gè)(可理解為多次調(diào)用該方法)
        repositoryHandler.maven {
            name '公司名稱'
            url "公司私有的maven倉庫地址"
            credentials { //配置倉庫用戶名和密碼
                username = 'myName'
                password = 'myPassword'
            }
        }
        repositoryHandler.ivy {} //ivy倉庫
        flatDir { dirs '../${項(xiàng)目名稱}/libs' } // aar等引入 ,這里只是聲明了存放aar的路徑枯途,還需要在使用到aar的module的build.gradle的dependencies中引入對應(yīng)的aar( implementation(name: 'aar名字', ext: 'aar')  )
        //注意如果是library類型的module中引入了aar,除了在該library module中引入忌怎,依賴了這個(gè)library的module中也需要再次引入
    }

    //配置工程的“插件”依賴地址,需要注意在這是scriptHandler的dependencies方法,而Project也有一個(gè)dependencies方法(即app module中的dependencies塊)酪夷。
    //兩者區(qū)別:我們的應(yīng)用程序的開發(fā)都會引入各種第三方的依賴庫榴啸,這些依賴庫在Project的dependencies方法中配置,
    //而Gradle實(shí)際上也是一個(gè)編程框架晚岭,那么在開發(fā)Gradle工程時(shí)也需要一些第三方的插件鸥印,這些插件就在scriptHandler的dependencies方法中配置
    scriptHandler.dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1' //引入這個(gè)gradle之后就可以將我們的工程指定為android類型module或者是library類型module
        classpath 'com.tencent.tinker:tinker-patch-gradle-plugin:1.9.1' //騰訊熱修復(fù)插件,引入熱修復(fù)補(bǔ)丁包生成插件工具
    }
}


//引用一個(gè)外部依賴需要使用 group, name 和 version 屬性. 如下
dependencies {
    compile group: 'com.android.support', name: 'appcompat-v7', version: '26.1.0'
}

//有一種簡寫形式坦报,只使用一串字符串 "group:name:version".如下,一般都是使用這種方式進(jìn)行配置
dependencies {
    compile 'com.android.support:appcompat-v7:26.1.0'
}


//app工程的build.gradle中
dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs') //添加文件樹依賴(本地的jar包)库说,另外還可以添加file和files依賴,取決于添加的依賴是文件樹還是單一文件或者多個(gè)文件
    implementation 'com.android.support.constraint:constraint-layout:1.1.3' //依賴遠(yuǎn)程jar包
    testImplementation 'junit:junit:4.12' //單元測試
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' //android中的測試
    implementation project(':ec')  //依賴源碼庫工程
    implementation project(':core') //依賴源碼庫工程
    annotationProcessor project(':compiler') //編譯期注解的依賴
    //optional, help to generate the final application 
    provided('com.tencent.tinker:tinker-android-anno:1.9.1') //為了在編譯的時(shí)候生成一個(gè)Application類燎竖,使用時(shí)不需要
    //tinker's main Android lib
    compile('com.tencent.tinker:tinker-android-lib:1.9.1')
    debugCompile 'com.amitshekhar.android:debug-db:1.0.0' //數(shù)據(jù)庫debug
}

  • Android Studio 2.x 版本和3.x依賴指令的區(qū)別

  • 2.x版本

  1. compile
    參與編譯和打包,使用compile方式依賴的第三方庫中所有的類要销,資源文件等都會被打包到項(xiàng)目的最終產(chǎn)物中(apk,jar,aar等)
  2. provided
    編譯占位构回,只參與編譯的過程,不會打包到apk,適用于只在編譯的時(shí)候起作用在運(yùn)行期不需要使用的庫,比如Tinker的tinker-android-anno纤掸,只為了在編譯的時(shí)候生成需要的Application類脐供,運(yùn)行時(shí)就不再需要這個(gè)工具了,另外適用于當(dāng)前工程需要引入一個(gè)主工程已經(jīng)引入的庫時(shí)借跪,比如在library module中作為工具類政己,先用provided占位,編譯通過掏愁,在使用的時(shí)候由用戶根據(jù)需求歇由,如果要使用該功能的話,再引入一次果港。
  3. apk
    只在生成apk的時(shí)候參與打包沦泌,編譯時(shí)不會參與
  4. testCompile
    參與單元測試代碼的編譯以及最終打包測試apk
  5. debugCompile
    參與debug模式的編譯和最終的debug apk打包
  6. releaseCompile
    參與release模式的編譯和最終的release apk打包
  • 3.x版本
  1. implementation
    對于使用了該命令編譯的依賴,對該項(xiàng)目有依賴的項(xiàng)目將無法訪問到使用該命令編譯的依賴中的任何程序辛掠,即將該依賴隱藏在內(nèi)部谢谦,而不對外部公開(非傳遞依賴)
    簡單來說,從Android Studio 3.X開始萝衩,依賴首先應(yīng)該設(shè)置為implement回挽,如果沒有錯(cuò)那就用implement,如果有錯(cuò)猩谊,那么使用api指令千劈,這樣會使編譯速度有所增快。假設(shè)有A预柒,B,C三個(gè)module,B,C依賴于A队塘,那么在A中使用implementation依賴的庫,在B,C中無法直接使用宜鸯,在A中使
    用implementation依賴的庫更改時(shí)憔古,只在A中重新編譯,在B,C中不需要編譯所以會加快編譯速度
  2. api
    同2.x版本的compile,參與編譯和打包淋袖,使用compile方式依賴的第三方庫中所有的類鸿市,資源文件等都會被打包到項(xiàng)目的最終產(chǎn)物中(apk,jar,aar等)
  3. compileOnly
    同2.x版本的provided,編譯占位,只參與編譯的過程即碗,不會打包到apk,適用于只在編譯的時(shí)候起作用在運(yùn)行期不需要使用的庫焰情,另外適用于當(dāng)前工程需要引入一個(gè)主工程已經(jīng)引入的庫時(shí)
  4. runtimeOnly
    同2.x版本的apk,只在生成apk的時(shí)候參與打包,編譯時(shí)不會參與
  5. testImplementation
    同2.x版本的testCompile,參與單元測試代碼的編譯以及最終打包測試apk
  6. debugImplementation
    同2.x版本的debugCompile,參與debug模式的編譯和最終的debug apk打包
  7. releaseImplementation
    同2.x版本的releaseCompile,參與release模式的編譯和最終的release apk打包
  8. annotationProcessor
    編譯注解依賴剥懒,只參與編譯過程内舟,不會打包到apk,目的是在編譯時(shí)動態(tài)生成代碼。
  • compileOnly(provided)annotationProcessor的區(qū)別
    都是只參與編譯期初橘,不會打包進(jìn)apk產(chǎn)物验游,而annotationProcessor只是為了在編譯期生成代碼充岛,compileOnly(provided)是在引入一個(gè)已經(jīng)被其它工程(主工程)引入的庫,那么使用該依賴指令讓該類庫在編譯期使用耕蝉,保證編譯通過崔梗,而在運(yùn)行期使用時(shí)使用的是被其它工程引入的庫,保證只引入該類庫一次垒在。
  • 依賴沖突和傳遞依賴的解決

    當(dāng)我們不小心重復(fù)引入了第三方庫時(shí)蒜魄,而這兩個(gè)第三方庫的版本不同的時(shí)候,就會導(dǎo)致依賴沖突

    我們依賴的第三方庫又依賴了其它第三方庫场躯,而它依賴的第三方庫是我們不需要使用的庫谈为,這個(gè)時(shí)候就需要解除傳遞依賴
//版本依賴沖突解決
//1. 強(qiáng)制依賴指定版本
configurations.all {
  resolutionStrategy.force 'com.android.support:support-annotations:26.1.0'
}

//完整寫法,排除multidex
 configurations.all {
        resolutionStrategy.eachDependency { DependencyResolveDetails details ->
            def requested = details.requested
            if (requested.group == 'com.android.support') {
                if (!requested.name.startsWith("multidex")) {
                    details.useVersion '26.1.0'
                }
            }
        }
    }


//2. 排除
implementation ('com.squareup.retrofit2:adapter-rxjava:2.1.0'){
        exclude group: 'io.reactivex' //排除指定包下的所有庫
    }
implementation 'io.reactivex.rxjava2:rxjava:2.0.6'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'

//另外如果沖突的是其中的一個(gè)module
implementation ('com.squareup.retrofit2:adapter-rxjava:2.1.0'){
        exclude module: '沖突的module name'  //排除指定module
    }

//傳遞依賴
//假設(shè)有工程A,B,C,工程A依賴了B推盛,而B又依賴了C峦阁,這個(gè)時(shí)候就構(gòu)成了傳遞依賴
//而在開發(fā)中一般是不允許A直接使用C的,因?yàn)榭赡蹷升級了之后耘成,不需要再依賴C了榔昔,如果在A中直接使用了工程C中的東西的話,就會導(dǎo)致各種編譯錯(cuò)誤瘪菌,因?yàn)锽已經(jīng)沒有依賴C了撒会,在A中使用的C中的東西都已經(jīng)找不到了
conpile ('com.squareup.retrofit2:adapter-rxjava:2.1.0'){
        transitive false //禁止傳遞依賴
    }
//在Gradle中默認(rèn)transitive就是false,另外在Android Studio 2.x 版本和3.x依賴指令的區(qū)別部分师妙,我們也可以知道诵肛,使用implementation,本身也是非傳遞依賴,在B中使用implementation依賴C默穴,A再依賴B怔檩,本身C中的API那些就無法在A中直接使用

  • 查看沖突的依賴庫
  1. 打印出該模塊所有的依賴樹信息:gradlew -q <模塊名>:dependencies
  2. 將項(xiàng)目切換至Project面板,在External Libraries目錄下查看
  3. 項(xiàng)目的Gradle Projects面板中運(yùn)行 Tasks->android->androidDependencies
  • 外部命令的執(zhí)行相關(guān)API
// 使用exec()方法執(zhí)行外部命令蓄诽,對于其它的javaexec方法薛训,只要配置好環(huán)境變量,實(shí)際上通過exec方法也是可以執(zhí)行的

task(name:'apkcopy'){ //創(chuàng)建一個(gè)名為apkcopy的task
    doLast{  //在task的最后執(zhí)行
        def sourcePath = this.buildDir.path+'/outputs/apk'
        def destinationPath = '/Users/wen/apks' //要存放的文件路徑
        def command = "mv -f $sourcePath $destinationPath" //命令行
        exec{
            try {
                executable 'bash'  //要使用的可執(zhí)行文件的名稱
                args '-c',command  //要執(zhí)行的命令的參數(shù)仑氛。默認(rèn)為空列表
                println 'the command is execute success'
            } catch (GradleException e) {
                println 'the command is execute failed'
            }
        }
    }
}


  • 執(zhí)行外部命令更多細(xì)節(jié)請點(diǎn)這里

  • Task相關(guān)API
    為Project提供新增Task和使用已有Task的能力乙埃。了解更多點(diǎn)擊 Gradle Task

  1. 什么是Task
    一個(gè)Task表示構(gòu)建的單個(gè)原子動作。構(gòu)建工作就是由各種Task構(gòu)成的锯岖。我們也可以定義各種各樣的Task介袜。在Gradle寫的其它腳本都會在配置階段被執(zhí)行(利用生命周期的監(jiān)聽除外),并且執(zhí)行的順序不確定出吹,而利用Task可以在執(zhí)行階段執(zhí)行我們需要的邏輯遇伞,可并為Task指定執(zhí)行順序。除此之外我們還可以為我們自己編寫的Task指定類型來讓Task具有更強(qiáng)的功能捶牢。
  1. Task定義和配置
//定義

//1. 通過task方法創(chuàng)建 
task getDateTask {
    println new Date().dateString //在終端輸入gradlew getDateTask 執(zhí)行Task輸出:18-10-16
}


//2.通過TaskContainer創(chuàng)建
this.tasks.create(name: 'getDateTask2') {
    println "getDateTask2 date: ${new Date().dateString}" 
}


//TaskContainer是用來管理一個(gè)Project中所有的Task的鸠珠,提供創(chuàng)建和查找task功能加派。查找對應(yīng)task最好是在配置階段之后,此時(shí)所有的task都配置好了跳芳。否則可能出現(xiàn)找不到對應(yīng)的task
task執(zhí)行結(jié)果.png
//配置
//1. 創(chuàng)建的時(shí)候直接配置
task getDateTask(group:'learn',description:'task study') {
    println new Date().dateString
}

task clean(type: Delete) { //配置類型
    delete rootProject.buildDir
}

//2. 方法配置
task getDateTask2 {
    setGroup('learn') //配置組
    setDescription('task study') //配置描述
    println new Date().dateString
}

  • 配置組之后同一組的Task會被放到同一目錄下(右側(cè)gradle面板中可以查看),description相當(dāng)于注釋竹勉,解釋該task的作用


    task分組.png
  • Task所有可配置信息
    String TASK_NAME = "name"; //名字

    String TASK_DESCRIPTION = "description"; //描述

    String TASK_GROUP = "group"; //所屬組

    String TASK_TYPE = "type";  //類型

    String TASK_DEPENDS_ON = "dependsOn";  //task依賴(依賴于其它task)

    String TASK_OVERWRITE = "overwrite"; //是否重寫其它task 默認(rèn)false

    String TASK_ACTION = "action"; //配置task相關(guān)邏輯
  • doFirst和doLast方法

    Task可以執(zhí)行在執(zhí)行階段飞盆,注意前面只執(zhí)行g(shù)etDateTask2,getDateTask也輸出了次乓,因?yàn)樵谂渲秒A段就會解析所有的Task用于構(gòu)建task拓?fù)鋱D吓歇,所以這些代碼會在配置階段就被執(zhí)行。下面利用doFirst和doLast方法讓腳本代碼執(zhí)行在執(zhí)行階段
task getDateTask(group: 'learn', description: 'task study') {
    println new Date().dateString
    //在閉包中調(diào)用
    doFirst { println "the group is:$group" }
}
//在閉包外調(diào)用
getDateTask.doFirst { println "the description is:$description" }
dofist.png
  • 可以看到票腰,task中沒有被doFirst包裹的代碼依舊在配置階段就被執(zhí)行了城看,而doFirst中的代碼是在執(zhí)行app中的getDateTask時(shí)執(zhí)行。并且在閉包外調(diào)用的doFirst先執(zhí)行

  • 至于doFirst和doLast的執(zhí)行區(qū)別看下面執(zhí)行結(jié)果相信大家就能明白了

task getDateTask(group: 'learn', description: 'task study') {
    println new Date().dateString
    doLast {println "the description2 is:$description"}
    //在閉包中調(diào)用
    doFirst { println "the group1 is:$group" }
    doFirst { println "the group2 is:$group" }
}
//在閉包外調(diào)用
getDateTask.doFirst { println "the description1 is:$description" }
doFirst和doLast的區(qū)別.png
  • doFirst和doLast最常用的地方是讓我們的腳本代碼執(zhí)行在已有Task的執(zhí)行之前或執(zhí)行之后
//統(tǒng)計(jì)項(xiàng)目構(gòu)建時(shí)間
def startBuildTime, endBuildTime
this.afterEvaluate { Project project ->
    //在配置階段之后(所有的task都被配置好杏慰,構(gòu)建了task拓?fù)鋱D)查找task 防止找不到對應(yīng)task
    def preBuildTask = project.tasks.getByName('preBuild') //構(gòu)建最先執(zhí)行preBuild task
    def buildTask = project.tasks.getByName('build') //構(gòu)建最終執(zhí)行build task

    preBuildTask.doFirst {
        startBuildTime = System.currentTimeMillis()
        println "build start,current time is $startBuildTime"
    }

    buildTask.doLast {
        endBuildTime = System.currentTimeMillis()
        println "build end,current time is $endBuildTime"

        println "The build took ${(endBuildTime - startBuildTime) / 1000} seconds"
    }
}

項(xiàng)目構(gòu)建時(shí)間.png
  • Task執(zhí)行順序

    在Gradle的配置階段是構(gòu)建所有Task的拓?fù)鋱D测柠,實(shí)際上可以理解為是在確定Task的執(zhí)行順序。那么Gradle是中各個(gè)Task的執(zhí)行順序是如何確定的缘滥。
  1. dependsOn配置依賴決定執(zhí)行順序

    在前面Task所有可配置內(nèi)容中有一個(gè)dependsOn屬性轰胁,這個(gè)屬性是用于配置Task依賴的,執(zhí)行一個(gè)task的時(shí)候朝扼,這個(gè)task所依賴的task會先被執(zhí)行赃阀,但是執(zhí)行它依賴的task對它是不會造成影響的
task taskA {
    doLast {
        println 'taskA run'
    }
}

task taskB {
    doLast {
        println 'taskB run'
    }
}

task taskC(dependsOn:[taskA,taskB]) { //為taskC配置依賴
    doLast {
        println 'taskC run'
    }
}
  • 可以看到單獨(dú)執(zhí)行沒有依賴的taskB只會執(zhí)行taskB,而執(zhí)行依賴了taskA、taskB的taskC,執(zhí)行之前首先會先執(zhí)行它依賴的taskA擎颖、taskB,而taskA和taskB的執(zhí)行順序?qū)嶋H上是隨機(jī)的


    Task依賴dependsOn.png
  • 當(dāng)事先不太清楚具體要依賴哪些Task,可以動態(tài)指定
//動態(tài)的指定依賴

task taskA(group:'gradle'){
    doLast {
        println 'taskA run'
    }
}

task taskB(group:'gradle'){
    doLast {
        println 'taskB run'
    }
}

task taskC(group:'java'){
    doLast {
        println 'taskC run'
    }
}

task taskD { //動態(tài)的指定task依賴
    dependsOn this.tasks.findAll {task -> task.group == 'gradle' }
    doLast {
        println 'taskD run'
    }
}
動態(tài)指定依賴.png
  1. mustRunAfter()榛斯、shouldRunAfter() API指定執(zhí)行順序

    需要注意的是,使用mustRunAfter和shouldRunAfter是在兩個(gè)task都會執(zhí)行的情境下會按照該指定順序執(zhí)行搂捧,調(diào)用了該Api指定依賴執(zhí)行順序的兩個(gè)task并沒有依賴關(guān)系驮俗。單獨(dú)執(zhí)行其中一個(gè)對另外的任務(wù)并不會有影響,但如果mustRunAfter和task依賴之間發(fā)生了沖突异旧,那么執(zhí)行時(shí)將會報(bào)錯(cuò)
task taskA{
    doLast{
        println 'taskA run'
    }
}

task taskB{
    doLast{
        println 'taskB run'
    }
}

task taskC{
    doLast{
        println 'taskC run'
    }
}

taskB.mustRunAfter(taskC)
taskA.mustRunAfter(taskB)
API指定task執(zhí)行排序.png
  • API指定task的排序意述,并不會指定它們之間的依賴關(guān)系,所以只執(zhí)行其中一個(gè)task吮蛹,'必須在它之前執(zhí)行的'task不會先被執(zhí)行荤崇,而執(zhí)行task,它依賴的task才會先被執(zhí)行,下面只執(zhí)行taskA,taskB和taskC都不會執(zhí)行
非依賴執(zhí)行.png
  • 常利用mustRunAfter()和dependsOn將我們的task插入到生命周期的task之間去完成特定的需求
//假設(shè)Gradle構(gòu)建的生命周期中有taskA和taskB,taskA執(zhí)行之后會執(zhí)行taskB,潮针,那么需要將myTask术荤,插入taskA和taskB中間

//以下是偽代碼
project.afterEvaluate{
  
    //1.找到taskA和taskB
    //可以通過TaskContainer的findTaskByName方法或其它方式獲取到響應(yīng)的task
    //假設(shè)已找到taskA和taskB
    
    //2.將myTask插入到taskA之后
    myTask.mustRunAfter taskA
    
   //3. 將myTask插入到taskB之前
   taskB.dependsOn myTask
}

  1. finalizedBy

    如果需要實(shí)現(xiàn)執(zhí)行某一個(gè)task之后必須執(zhí)行另一個(gè)task,用上面的方法是沒辦法的每篷,此時(shí)可以利用finalizedBy API
task taskA{
    doLast{
        println 'taskA run'
    }
}

task taskB{
    doLast{
        println 'taskB run'
    }
}

//執(zhí)行完taskA之后必須執(zhí)行taskB
taskA.finalizedBy taskB
執(zhí)行taskA后taskB跟著執(zhí)行.png
  • TaskInputs瓣戚、TaskOutputs
    輸入輸出在Gradle中用于增量構(gòu)建端圈,執(zhí)行一個(gè)任務(wù),如果在編譯時(shí)子库,gradle判斷在從上一次編譯中舱权,該task的輸入輸出沒有任何改變,那么gradle就會跳過該task仑嗅,前提是這個(gè)task至少有一個(gè)輸入或輸出

  • Task源碼中獲取輸入輸出的方法

  /**
     * <p>Returns the inputs of this task.</p>
     *
     * @return The inputs. Never returns null.
     */
    @Internal
    TaskInputs getInputs();

    /**
     * <p>Returns the outputs of this task.</p>
     *
     * @return The outputs. Never returns null.
     */
    @Internal
    TaskOutputs getOutputs();
  • TaskInputs支持的輸入類型
  /**
     * 返回所有task的輸入文件
     *
     * @return 返回輸入文件宴倍,如果task沒有輸入文件返回一個(gè)空的文件集合
     */
    FileCollection getFiles();

    /**
     * 指定該task的輸入文件(多個(gè)文件)
     *
     * @param 輸入文件路徑
     * @return 返回property builder以進(jìn)一步配置屬性
     */
    TaskInputFilePropertyBuilder files(Object... paths);

    /**
     *指定該task的輸入文件(一個(gè)文件)
     *
     * @param 輸入文件路徑. 
     * @return 返回property builder以進(jìn)一步配置屬性
     */
    TaskInputFilePropertyBuilder file(Object path);

    /**
     * 為該task注冊一個(gè)輸入目錄。在給定目錄下找到的所有文件都被視為輸入文件
     *
     * @return 返回property builder以進(jìn)一步配置屬性
     */
    TaskInputFilePropertyBuilder dir(Object dirPath);

    /**
     * 返回該task的輸入屬性集仓技。
     *
     * @return The properties.
     */
    Map<String, Object> getProperties();

    /**
     * 為該task注冊一個(gè)輸入屬性鸵贬。該屬性值在task執(zhí)行時(shí)持久化,并task的后
     * 調(diào)用的屬性值進(jìn)行比較脖捻,以確定task是否最新阔逼。
     *
     *屬性的給定值必須是可序列化的,并且應(yīng)該提供一個(gè)有效的equal()方法
     *
     *也可指定一個(gè)閉包作為屬性的值,在這種情況下會執(zhí)行該閉包用于確定屬性的值
     *
     * @param name The name of the property. Must not be null.
     * @param value The value for the property. Can be null.
     */
    TaskInputs property(String name, Object value);

    /**
     * 為該Task注冊一個(gè)輸入屬性集合
     *
     * @param properties The properties.
     */
    TaskInputs properties(Map<String, ?> properties);
    
    //注意TaskInputFilePropertyBuilder繼承于TaskInputs地沮,用于描述包含零個(gè)或多個(gè)文件的task的輸入屬性嗜浮。
  • TaskOutputs支持的輸出類型
  /**
     * 返回該Task的所有輸出文件
     *
     * @return The output files. Returns an empty collection if this task has no output files.
     */
    FileCollection getFiles();

    /**
     * 指定該Task的輸出是文件(多個(gè))
     *
     *如果是一個(gè)map,key必須是有效的java標(biāo)識符摩疑,否則task輸出緩存會被禁用
     *
     * @param paths The output files.
     *
     * @see CacheableTask
     */
    TaskOutputFilePropertyBuilder files(Object... paths);

    /**
     * 該Task輸出是文件目錄
     *
     *
     * @param paths The output files.
     *
     * @see CacheableTask
     *
     */
    TaskOutputFilePropertyBuilder dirs(Object... paths);

    /**
     * 指定該Task的輸出是文件(單個(gè))
     *
     */
    TaskOutputFilePropertyBuilder file(Object path);

    /**
     * task輸出文件是dir
     */
    TaskOutputFilePropertyBuilder dir(Object path);
    
    //注意TaskOutputFilePropertyBuilder繼承于TaskOutputs,用于描述包含零個(gè)或多個(gè)文件的task的輸出屬性周伦。
  • 總結(jié):TaskInputs支持單個(gè)文件、多個(gè)文件和文件目錄以及以key-value形式的單一屬性和key-value形式的屬性map,需要注意這些key-value都需要是serializable的未荒,因?yàn)樾枰涗浧饋碜ㄅ玻籘askOutputs支持單個(gè)文件、多個(gè)文件和文件目錄
  • SourceSet修改工程中各項(xiàng)資源文件的位置
    當(dāng)我們新建一個(gè)Android項(xiàng)目的時(shí)候片排,AS幫我們創(chuàng)建一系列的目錄和文件寨腔,比如放置代碼的java目錄,放置資源的res目錄率寡,main目錄下AndroidManifest.xml文件等迫卢,那么為什么編寫的代碼文件就需要放置在java目錄,資源就要放入res目錄呢?是因?yàn)榫幾g項(xiàng)目時(shí)冶共,Gradle默認(rèn)會從這些相應(yīng)的目錄中加載相應(yīng)的代碼和資源乾蛤,這是約定好的,如果我們沒有做任何改動捅僵,就會按照它默認(rèn)的方式去加載代碼和資源家卖。所以我們可以根據(jù)我們的需要修改這些默認(rèn)的選項(xiàng),借助SourceSet提供的方法庙楚,我們可以按照我們想要的方式來配置代碼和資源的加載目錄
  //在AS中上荡,gradle默認(rèn)去加載jniLibs目錄中的 *.so 文件
    sourceSets { //soutceSet方法重定位資源文件的位置
        main {//jniLibs在main目錄下,所以需要調(diào)用main方法
            jniLibs.srcDirs = ['libs'] //修改到libs目錄中加載*.so文件
        }
    }
    
 //修改res的目錄馒闷,有時(shí)候?yàn)榱吮阌诠芾砝壹瘢瑔为?dú)的一個(gè)模塊的資源文件都放在一個(gè)單獨(dú)的目錄中叁征,想要gradle能夠正確識別到這些文件,需要而外添加進(jìn)來   
     sourceSets {
        main {
        //指定從多個(gè)路徑加載資源文件
            res.srcDirs = [  
                    'src/main/res/chat-res',
                    'src/main/res'
            ]
        }
    }
  • 構(gòu)建變體
    對于Android開發(fā)者來說捺疼,項(xiàng)目最終打包成APK,了解這個(gè)APK的各個(gè)屬性的配置尤為重要永罚。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末帅涂,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子尤蛮,更是在濱河造成了極大的恐慌,老刑警劉巖斯议,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件产捞,死亡現(xiàn)場離奇詭異,居然都是意外死亡哼御,警方通過查閱死者的電腦和手機(jī)坯临,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來恋昼,“玉大人看靠,你說我怎么就攤上這事∫杭。” “怎么了挟炬?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長嗦哆。 經(jīng)常有香客問我谤祖,道長,這世上最難降的妖魔是什么老速? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任粥喜,我火速辦了婚禮,結(jié)果婚禮上橘券,老公的妹妹穿的比我還像新娘额湘。我一直安慰自己,他們只是感情好旁舰,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布锋华。 她就那樣靜靜地躺著,像睡著了一般箭窜。 火紅的嫁衣襯著肌膚如雪供置。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天绽快,我揣著相機(jī)與錄音芥丧,去河邊找鬼紧阔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛续担,可吹牛的內(nèi)容都是我干的擅耽。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼物遇,長吁一口氣:“原來是場噩夢啊……” “哼乖仇!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起询兴,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤乃沙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后诗舰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體警儒,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年眶根,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蜀铲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,117評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡属百,死狀恐怖记劝,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情族扰,我是刑警寧澤厌丑,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站渔呵,受9級特大地震影響蹄衷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜厘肮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一愧口、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧类茂,春花似錦耍属、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至兢哭,卻和暖如春领舰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工冲秽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留舍咖,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓锉桑,卻偏偏與公主長得像排霉,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子民轴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評論 2 345

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

  • 說明 本文主要介紹和Gradle關(guān)系密切攻柠、相對不容易理解的配置,偏重概念介紹后裸。部分內(nèi)容是Android特有的(例如...
    jzj1993閱讀 15,577評論 1 62
  • 在 Android Studio 構(gòu)建的項(xiàng)目中瑰钮,基于 Gradle 進(jìn)行項(xiàng)目的構(gòu)建,同時(shí)使用 Android DS...
    Ant_way閱讀 7,329評論 0 16
  • 參考資料:http://gold.xitu.io/post/580c85768ac247005b5472f9htt...
    zhaoyubetter閱讀 10,979評論 0 6
  • Gradle 是一款構(gòu)建系統(tǒng)工具微驶,它的 DSL 基于 Groovy 實(shí)現(xiàn)浪谴。Gradle 構(gòu)建的大部分功能都是通過插...
    任教主來也閱讀 3,042評論 3 6
  • Gradle是基于Groovy的動態(tài)DSL,而Groovy是基于JVM的祈搜,Groovy的語法和Java很類似。 C...
    HoooChan閱讀 7,446評論 0 7