初識Gradle
一、Gradle的基本概念
一個開源的專案自動化構建工具,建立在Apache Ant和Apache Maven概念的基礎上,並引入了基於Groovy的特定領域語言(DSL),而不再使用XML形式管理構建指令碼。同時,gradle還是一個程式設計框架,可以讓開發者使用程式設計的思想來實現應用構建。gradle的組成:
- groovy核心語法
- build script block
- 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的使用場景:
- 依賴包只在編譯期起作用。(如:tinker的tinker-android-anno只用於在編譯期生成Application,並不需要把該庫中類打包進apk,這樣可以減小apk包體積)
- 被依賴的工程中已經有了相同版本的第三方庫,為了避免重複引用,可以使用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.'
}
}
}
}
複製程式碼