七、Gradle中的Project

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

一、Project概念

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

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

    settings.gradle.png
  • 可以看到settings.gradle中include了app、annottations(小手一抖多了個(gè)t,注意一下就好annotations)、compiler、core、ec,那么我們的項(xiàng)目中有幾個(gè)Project對(duì)象呢?是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最終會(huì)被Gradle編譯為Project字節(jié)碼
  1. Project的作用
    根Project是用來統(tǒng)籌管理所有的子Project的,而每個(gè)子Project都對(duì)應(yīng)了一個(gè)輸出。比如我們的app module的類型是application的,那么它最終就對(duì)應(yīng)生成了一個(gè)APK文件,是android library類型的,最終會(huì)生成一個(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,會(huì)走Gradle的生命周期,而配置階段是需要解析所有Project的所有Task,生成拓?fù)鋱D,前面也有提到,build.gradle最終會(huì)被Gradle編譯為Project字節(jié)碼,所以我們?cè)赽uild.gradle中編寫我們的腳本,實(shí)際上就是在Project內(nèi)部編寫。也就是說我們?cè)赼pp module的build.gradle調(diào)用編寫的方法的腳本,會(huì)在解析的時(shí)候被調(diào)用,所以即使是我們跑Task clean,也會(huì)輸出我們想要的結(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的名字,然后及對(duì)該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ù)的代碼,我們都會(huì)想辦法將它們提取出來。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 對(duì)應(yīng) GROUP_ID
//support-annotations 對(duì)應(yīng) ARTIFACT_ID
//26.1.0 對(duì)應(yīng) VERSION
//而getRepositoryUrl()獲取的是你的Maven倉庫URL,userName和password分別對(duì)應(yīng)Maven倉庫的用戶名和密碼,上傳到Maven倉庫之前需要有你自己Maven倉庫,這一步大家google一下基本都會(huì)了,當(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的,那么對(duì)于我們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è)屬性基本上不會(huì)使用到
     */
    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在配置階段會(huì)被解析成Project字節(jié)碼,所以在這里的配置實(shí)際上是在Project類中編寫代碼,而不僅僅是配置。
//寫代碼的話,里面的一些寫死的魔法值如編譯sdk版本26,還有寫死的字符串a(chǎn)ppcompat-v7依賴com.android.support:appcompat-v7:26.1.0都是不符合編碼風(fēng)格的
//尤其是許多依賴的版本號(hào)都是一樣的,如各種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)勢(shì)就可以提現(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中,根工程定義的屬性會(huì)被子工程繼承
//一個(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會(huì)在debug apk的manifest文件application標(biāo)簽里自動(dòng)添加
//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()
   }
}

  • 對(duì)于gradle屬性文件gradle.properties,想要了解更多點(diǎn)擊構(gòu)建環(huán)境
  • 另外一個(gè)local.properties,用于構(gòu)建系統(tǒng)配置本地環(huán)境屬性,例如SDK安裝路徑。由于該文件的內(nèi)容由AS自動(dòng)生成并且專用于本地開發(fā)者環(huán)境,因此不應(yīng)手動(dò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目錄下找,找不到會(huì)報(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í)候會(huì)自動(dòng)為我們配置一些東西

//根工程的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 會(huì)根據(jù)依賴定義的順序在各個(gè)庫里尋找它們。在第一個(gè)庫里找到了就不會(huì)再在第二個(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中引入對(duì)應(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ā)都會(huì)引入各種第三方的依賴庫,這些依賴庫在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' //單元測(cè)試
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' //android中的測(cè)試
    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方式依賴的第三方庫中所有的類,資源文件等都會(huì)被打包到項(xiàng)目的最終產(chǎn)物中(apk,jar,aar等)
  2. provided
    編譯占位,只參與編譯的過程,不會(huì)打包到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í)不會(huì)參與
  4. testCompile
    參與單元測(cè)試代碼的編譯以及最終打包測(cè)試apk
  5. debugCompile
    參與debug模式的編譯和最終的debug apk打包
  6. releaseCompile
    參與release模式的編譯和最終的release apk打包
  • 3.x版本
  1. implementation
    對(duì)于使用了該命令編譯的依賴,對(duì)該項(xiàng)目有依賴的項(xiàng)目將無法訪問到使用該命令編譯的依賴中的任何程序,即將該依賴隱藏在內(nèi)部,而不對(duì)外部公開(非傳遞依賴)
    簡單來說,從Android Studio 3.X開始,依賴首先應(yīng)該設(shè)置為implement,如果沒有錯(cuò)那就用implement,如果有錯(cuò),那么使用api指令,這樣會(huì)使編譯速度有所增快。假設(shè)有A,B,C三個(gè)module,B,C依賴于A,那么在A中使用implementation依賴的庫,在B,C中無法直接使用,在A中使
    用implementation依賴的庫更改時(shí),只在A中重新編譯,在B,C中不需要編譯所以會(huì)加快編譯速度
  2. api
    同2.x版本的compile,參與編譯和打包,使用compile方式依賴的第三方庫中所有的類,資源文件等都會(huì)被打包到項(xiàng)目的最終產(chǎn)物中(apk,jar,aar等)
  3. compileOnly
    同2.x版本的provided,編譯占位,只參與編譯的過程,不會(huì)打包到apk,適用于只在編譯的時(shí)候起作用在運(yùn)行期不需要使用的庫,另外適用于當(dāng)前工程需要引入一個(gè)主工程已經(jīng)引入的庫時(shí)
  4. runtimeOnly
    同2.x版本的apk,只在生成apk的時(shí)候參與打包,編譯時(shí)不會(huì)參與
  5. testImplementation
    同2.x版本的testCompile,參與單元測(cè)試代碼的編譯以及最終打包測(cè)試apk
  6. debugImplementation
    同2.x版本的debugCompile,參與debug模式的編譯和最終的debug apk打包
  7. releaseImplementation
    同2.x版本的releaseCompile,參與release模式的編譯和最終的release apk打包
  8. annotationProcessor
    編譯注解依賴,只參與編譯過程,不會(huì)打包到apk,目的是在編譯時(shí)動(dòng)態(tài)生成代碼。
  • compileOnly(provided)annotationProcessor的區(qū)別
    都是只參與編譯期,不會(huì)打包進(jìn)apk產(chǎn)物,而annotationProcessor只是為了在編譯期生成代碼,compileOnly(provided)是在引入一個(gè)已經(jīng)被其它工程(主工程)引入的庫,那么使用該依賴指令讓該類庫在編譯期使用,保證編譯通過,而在運(yùn)行期使用時(shí)使用的是被其它工程引入的庫,保證只引入該類庫一次。
  • 依賴沖突和傳遞依賴的解決

    當(dāng)我們不小心重復(fù)引入了第三方庫時(shí),而這兩個(gè)第三方庫的版本不同的時(shí)候,就會(huì)導(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)榭赡蹷升級(jí)了之后,不需要再依賴C了,如果在A中直接使用了工程C中的東西的話,就會(huì)導(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í)行外部命令,對(duì)于其它的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é)請(qǐng)點(diǎn)這里

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

  1. 什么是Task
    一個(gè)Task表示構(gòu)建的單個(gè)原子動(dòng)作。構(gòu)建工作就是由各種Task構(gòu)成的。我們也可以定義各種各樣的Task。在Gradle寫的其它腳本都會(huì)在配置階段被執(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功能。查找對(duì)應(yīng)task最好是在配置階段之后,此時(shí)所有的task都配置好了。否則可能出現(xiàn)找不到對(duì)應(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會(huì)被放到同一目錄下(右側(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段就會(huì)解析所有的Task用于構(gòu)建task拓?fù)鋱D,所以這些代碼會(huì)在配置階段就被執(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 防止找不到對(duì)應(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會(huì)先被執(zhí)行,但是執(zhí)行它依賴的task對(duì)它是不會(huì)造成影響的
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只會(huì)執(zhí)行taskB,而執(zhí)行依賴了taskA、taskB的taskC,執(zhí)行之前首先會(huì)先執(zhí)行它依賴的taskA、taskB,而taskA和taskB的執(zhí)行順序?qū)嶋H上是隨機(jī)的


    Task依賴dependsOn.png
  • 當(dāng)事先不太清楚具體要依賴哪些Task,可以動(dòng)態(tài)指定
//動(dòng)態(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 { //動(dòng)態(tài)的指定task依賴
    dependsOn this.tasks.findAll {task -> task.group == 'gradle' }
    doLast {
        println 'taskD run'
    }
}
動(dòng)態(tài)指定依賴.png
  1. mustRunAfter()、shouldRunAfter() API指定執(zhí)行順序

    需要注意的是,使用mustRunAfter和shouldRunAfter是在兩個(gè)task都會(huì)執(zhí)行的情境下會(huì)按照該指定順序執(zhí)行,調(diào)用了該Api指定依賴執(zhí)行順序的兩個(gè)task并沒有依賴關(guān)系。單獨(dú)執(zhí)行其中一個(gè)對(duì)另外的任務(wù)并不會(huì)有影響,但如果mustRunAfter和task依賴之間發(fā)生了沖突,那么執(zhí)行時(shí)將會(huì)報(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的排序,并不會(huì)指定它們之間的依賴關(guān)系,所以只執(zhí)行其中一個(gè)task,'必須在它之前執(zhí)行的'task不會(huì)先被執(zhí)行,而執(zhí)行task,它依賴的task才會(huì)先被執(zhí)行,下面只執(zhí)行taskA,taskB和taskC都不會(huì)執(zhí)行
非依賴執(zhí)行.png
  • 常利用mustRunAfter()和dependsOn將我們的task插入到生命周期的task之間去完成特定的需求
//假設(shè)Gradle構(gòu)建的生命周期中有taskA和taskB,taskA執(zhí)行之后會(huì)執(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就會(huì)跳過該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注冊(cè)一個(gè)輸入目錄。在給定目錄下找到的所有文件都被視為輸入文件
     *
     * @return 返回property builder以進(jìn)一步配置屬性
     */
    TaskInputFilePropertyBuilder dir(Object dirPath);

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

    /**
     * 為該task注冊(cè)一個(gè)輸入屬性。該屬性值在task執(zhí)行時(shí)持久化,并task的后
     * 調(diào)用的屬性值進(jìn)行比較,以確定task是否最新。
     *
     *屬性的給定值必須是可序列化的,并且應(yīng)該提供一個(gè)有效的equal()方法
     *
     *也可指定一個(gè)閉包作為屬性的值,在這種情況下會(huì)執(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注冊(cè)一個(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)識(shí)符,否則task輸出緩存會(huì)被禁用
     *
     * @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)樾枰涗浧饋?;TaskOutputs支持單個(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)會(huì)從這些相應(yīng)的目錄中加載相應(yīng)的代碼和資源,這是約定好的,如果我們沒有做任何改動(dòng),就會(huì)按照它默認(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能夠正確識(shí)別到這些文件,需要而外添加進(jìn)來   
     sourceSets {
        main {
        //指定從多個(gè)路徑加載資源文件
            res.srcDirs = [  
                    'src/main/res/chat-res',
                    'src/main/res'
            ]
        }
    }
  • 構(gòu)建變體
    對(duì)于Android開發(fā)者來說,項(xiàng)目最終打包成APK,了解這個(gè)APK的各個(gè)屬性的配置尤為重要。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容