Gradle入門系列(三)——初識Gradle與Project

GitLqr發表於2019-01-05

初識Gradle

一、Gradle的基本概念

一個開源的專案自動化構建工具,建立在Apache Ant和Apache Maven概念的基礎上,並引入了基於Groovy的特定領域語言(DSL),而不再使用XML形式管理構建指令碼。同時,gradle還是一個程式設計框架,可以讓開發者使用程式設計的思想來實現應用構建。gradle的組成:

  1. groovy核心語法
  2. build script block
  3. gradle api

二、Gradle的執行流程

生命週期 作用
Initialzation初始化階段 解析整個工程中所有project(讀取setting.gradle檔案),構建所有的project對應的Project物件
Configuration配置階段 解析所有的project物件中的task,構建好所有task的拓撲圖
Execution執行階段 執行具體的task及其依賴的task

生命週期監聽:

// 配置階段開始前的監聽回撥(即:在Initialzation與Configuration之間)
this.beforeEvaluate {}
// 配置階段完成後的監聽回撥(即:在Configuration與Execution之間)
this.afterEvaluate {}
// gradle執行完畢後的回撥監聽(即:在Execution之後)
this.gradle.buildFinished {}

// 與 this.beforeEvaluate {} 一樣
this.gradle.beforeProject {}
// 與 this.afterEvaluate {} 一樣
this.gradle.afterProject {}
複製程式碼

Gradle中的Project

一、Idea與Gradle 對於project概念的區別

在Idea中,一個專案就是Project,一個Project下可以包含多個模組(Module),一個Module下,又可以有多個Module,其樹狀結構如下:

雖然可以在Module中繼續建立子Module,但一般情況下,我們不會這麼做,最多就兩層。

+Project
--+Module
----+Module
----+Module
--+Module
--+Module
複製程式碼

而對於Gradle而言,Idea中的無論是Project還是Module,都是project,故樹狀結構如下:

每個project下,都一定會有一個build.gradle

+project        // rootProject
--+project      // subProject
----+project
----+project
--+project
--+project
複製程式碼

二、project相關api

api 作用
getAllprojects() 獲取工程中所有的project(包括根project與子project)
getSubProjects() 獲取當前project下,所有的子project(在不同的project下呼叫,結果會不一樣,可能返回null)
getParent() 獲取當前project的父project(若在rooProject的build.gradle呼叫,則返回null)
getRootProject() 獲取專案的根project(一定不會為null)
project(String path, Closure configureClosure) 根據path找到project,通過閉包進行配置(閉包的引數是path對應的Project物件)
allprojects(Closure configureClosure) 配置當前project和其子project的所有project
subprojects(Closure configureClosure) 配置子project的所有project(不包含當前project)
// rootProject build.gradle下配置:
// 1、Project project(String path, Closure configureClosure);
project('app') { Project project ->       // 一個引數時,可以省略不寫,這裡只是為了明確引數的型別
  apply plugin : 'com.android.application'
  group 'com.lqr'
  version '1.0.0-release'
  dependencies {}
  android {}
}

// 2、allprojects(Closure configureClosure)
allprojects {
  group 'com.lqr'
  version '1.0.0-release'
}

// 3、subprojects(Closure configureClosure)
subprojects { Project project -> 
  if(project.plugins.hasPlugin('com.android.library')){
    apply from: '../publishToMaven.gradle'
  }
}
複製程式碼

三、屬性相關api

1、在gradle指令碼檔案中使用ext塊擴充套件屬性

父project中通過ext塊定義的屬性,子project可以直接訪問使用

// rootProject : build.gradle
// 定義擴充套件屬性
ext {
  compileSdkVersion = 25
  libAndroidDesign = 'com.android.support:design:25.0.0'
}

// app : build.gradle
android {
  compileSdkVersion = this.compileSdkVersion // 父project中的屬性,子project可以直接訪問使用
  ...
}
dependencies {
  compile this.libAndroidDesign // 也可以使用:this.rootproject.libAndroidDesign
  ...
}

複製程式碼

2、在gradle.properties檔案中擴充套件屬性

hasProperty('xxx'):判斷是否有在gradle.properties檔案定義xxx屬性。 在gradle.properties中定義的屬性,可以直接訪問,但得到的型別為Object,一般需要通過toXXX()方法轉型。

// gradle.properties
// 定義擴充套件屬性
isLoadTest=true
mCompileSdkVersion=25

// setting.gradle
// 判斷是否需要引入Test這個Module
if(hasProperty('isLoadTest') ? isLoadTest.toBoolean() : false) {
  include ':Test'
}

// app : build.gradle
android {
  compileSdkVersion = mCompileSdkVersion.toInteger()
  ...
}
複製程式碼

四、檔案相關api

api 作用
getRootDir() 獲取rootProject目錄
getBuildDir() 獲取當前project的build目錄(每個project都有自己的build目錄)
getProjectDir() 獲取當前project目錄
File file(Object path) 定位一個檔案,相對於當前project開始查詢
ConfigurableFileCollection files(Object... paths) 定位多個檔案,與file類似
copy(Closure closure) 拷貝檔案
fileTree(Object baseDir, Closure configureClosure) 定位一個檔案樹(目錄+檔案),可對檔案樹進行遍歷
// 列印common.gradle檔案內容
println getContent('common.gradle')
def getContent(String path){
  try{
    def file = file(path)
    return file.text
  }catch(GradleException e){
    println 'file not found..'
  }
  return null
}

// 拷貝檔案、資料夾
copy {
  from file('build/outputs/apk/')
  into getRootProject().getBuildDir().path + '/apk/'
  exclude {} // 排除檔案
  rename {} // 檔案重新命名
}

// 對檔案樹進行遍歷並拷貝
fileTree('build/outputs/apk/') { FileTree fileTree ->
    fileTree.visit { FileTreeElement element ->
        println 'the file name is: '+element.file.name
        copy {
            from element.file
            into getRootProject().getBuildDir().path + '/test/'
        }
    }
}
複製程式碼

五、依賴相關api

配置工程倉庫及gradle外掛依賴

// rootProject : build.gradle
buildscript { ScriptHandler scriptHandler ->
    // 配置工程倉庫地址
    scriptHandler.repositories { RepositoryHandler repositoryHandler ->
        repositoryHandler.jcenter()
        repositoryHandler.mavenCentral()
        repositoryHandler.mavenLocal()
        repositoryHandler.ivy {}
        repositoryHandler.maven { MavenArtifactRepository mavenArtifactRepository ->
            mavenArtifactRepository.name 'personal'
            mavenArtifactRepository.url 'http://localhost:8081/nexus/repositories/'
            mavenArtifactRepository.credentials {
                username = 'admin'
                password = 'admin123'
            }
        }
    }
    // 配置工程的"外掛"(編寫gradle指令碼使用的第三方庫)依賴地址
    scriptHandler.dependencies {
        classpath 'com.android.tools.build:gradle:2.2.2'
        classpath 'com.tencent.tinker-patch-gradle-plugin:1.7.7'
    }
}

// ============ 上述指令碼簡化後 ============

buildscript {
    // 配置工程倉庫地址
    repositories {
        jcenter()
        mavenCentral()
        mavenLocal()
        ivy {}
        maven {
            name 'personal'
            url 'http://localhost:8081/nexus/repositories/'
            credentials {
                username = 'admin'
                password = 'admin123'
            }
        }
    }
    // 配置工程的"外掛"(編寫gradle指令碼使用的第三方庫)依賴地址
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.2'
        classpath 'com.tencent.tinker-patch-gradle-plugin:1.7.7'
    }
}
複製程式碼

配置應用程式第三方庫依賴

compile: 編譯依賴包並將依賴包中的類打包進apk。 provided: 只提供編譯支援,但打包時依賴包中的類不會寫入apk。

// app : build.gradle
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar']) // 依賴檔案樹
    // compile file() // 依賴單個檔案
    // compile files() // 依賴多個檔案
    compile 'com.android.support:appcompat-v7:26.1.0' // 依賴倉庫中的第三方庫(即:遠端庫)
    compile project('mySDK') { // 依賴工程下其他Module(即:原始碼庫工程)
      exclude module: 'support-v4' // 排除依賴:排除指定module
      exclude group: 'com.android.support' // 排除依賴:排除指定group下所有的module
      transitive false // 禁止傳遞依賴,預設值為false
    }
  
    // 棧內編譯
    provided('com.tencent.tinker:tinker-android-anno:1.9.1')
}
複製程式碼

provided的使用場景:

  1. 依賴包只在編譯期起作用。(如:tinker的tinker-android-anno只用於在編譯期生成Application,並不需要把該庫中類打包進apk,這樣可以減小apk包體積)
  2. 被依賴的工程中已經有了相同版本的第三方庫,為了避免重複引用,可以使用provided。

六、外部命令api

// copyApk任務:用於將app工程生成出來apk目錄及檔案拷貝到本機下載目錄
task('copyApk') {
    doLast {
        // gradle的執行階段去執行
        def sourcePath = this.buildDir.path + '/outputs/apk'
        def destinationPath = '/Users/lqr/Downloads'
        def command = "mv -f ${sourcePath} ${destinationPath}"
        // exec塊程式碼基本是固定的
        exec {
            try {
                executable 'bash'
                args '-c', command
                println 'the command is executed success.'
            }catch (GradleException e){
                println 'the command is executed failed.'
            }
        }
    }
}
複製程式碼

相關文章