項目和任務(wù)
Gradle中的所有內(nèi)容都基于兩個基本概念:項目和任務(wù)。
每個Gradle構(gòu)建都由一個或多個項目組成。項目代表什么取決于您使用Gradle做什么。例如,項目可能表示JAR或Web應(yīng)用程序。
每個項目由一個或多個任務(wù)組成。任務(wù)代表構(gòu)建執(zhí)行的一些原子工作。這可能是編譯某些類,創(chuàng)建JAR,生成Javadoc或?qū)⒁恍┐鏅n發(fā)布到存儲庫。
Hello,World
您使用gradle命令運行Gradle構(gòu)建。gradle命令查找當(dāng)前目錄中的build.gradle文件。我們稱這個build.gradle文件為構(gòu)建腳本,但嚴(yán)格來說它是一個構(gòu)建配置腳本
task hello {
doLast {
println 'Hello world!'
}
}
命令行shell中,執(zhí)行gradle -q hello
n$ gradle -q hello
Hello world!
任務(wù)依賴
您可以聲明依賴于其他任務(wù)的任務(wù)。
task hello {
doLast {
println 'Hello world!'
}
}
task intro {
dependsOn hello
doLast {
println "I'm Gradle"
}
}
輸出
$ gradle -q intro
Hello world!
I'm Gradle
依賴尚不存在的任務(wù)
task taskX {
dependsOn 'taskY'
doLast {
println 'taskX'
}
}
task taskY {
doLast {
println 'taskY'
}
}
$ gradle -q taskX
taskY
taskX
可以在定義之前聲明taskX 的依賴關(guān)系。這種自由對于多項目構(gòu)建非常重要。
動態(tài)任務(wù)
您還可以使用動態(tài)創(chuàng)建任務(wù)。
4.times { counter ->
task "task$counter" {
doLast {
println "I'm task number $counter"
}
}
}
$ gradle -q task1
I'm task number 1
您可以在運行時動態(tài)地向任務(wù)添加依賴項
4.times { counter ->
task "task$counter" {
doLast {
println "I'm task number $counter"
}
}
}
task0.dependsOn task2, task3
$ gradle -q task0
I'm task number 2
I'm task number 3
I'm task number 0
或者,您可以向現(xiàn)有任務(wù)添加行為。
task hello {
doLast {
println 'Hello Earth'
}
}
hello.doFirst {
println 'Hello Venus'
}
hello.configure {
doLast {
println 'Hello Mars'
}
}
hello.configure {
doLast {
println 'Hello Jupiter'
}
}
$ gradle -q hello
Hello Venus
Hello Earth
Hello Mars
Hello Jupiter
doFirst和doLast可以執(zhí)行多次。他們將操作添加到任務(wù)的操作列表的開頭或結(jié)尾。執(zhí)行任務(wù)時,將按順序執(zhí)行操作列表中的操作。
Groovy DSL快捷方式表示法
有一種方便的表示法來訪問現(xiàn)有任務(wù)。每個任務(wù)都可以作為構(gòu)建腳本的屬性使用:
將任務(wù)作為構(gòu)建腳本的屬性進行訪問
task hello {
doLast {
println 'Hello world!'
}
}
hello.doLast {
println "Greetings from the $it.name task."
}
$ gradle -q hello
Hello world!
Greetings from the hello task.
默認(rèn)任務(wù)
Gradle允許您定義在未指定其他任務(wù)時執(zhí)行的一個或多個默認(rèn)任務(wù)。
defaultTasks 'clean', 'run'
task clean {
doLast {
println 'Default Cleaning!'
}
}
task run {
doLast {
println 'Default Running!'
}
}
task other {
doLast {
println "I'm not a default task!"
}
}
$ gradle -q
Default Cleaning!
Default Running!
gradle 項目分析
├── app
│ ├── app.iml
│ ├── build.gradle
│ ├── libs
│ ├── proguard-rules.pro
│ └── src
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ └── binzi
│ │ │ └── gradle
│ │ │ └── MainActivity.java
│ │ └── res
├── build.gradle
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.iml
├── gradle.properties
├── gradlew
├── gradlew.bat
├── local.properties
└── settings.gradle
settings.gradle
settings.gradle 是負(fù)責(zé)配置項目的腳本
對應(yīng) Settings類,gradle 構(gòu)建過程中,會根據(jù) settings.gradle 生成 Settings 的對象
其中幾個主要的方法有:
- include(projectPaths)
- includeFlat(projectNames)
- project(projectDir)
一般在項目里見到的引用子模塊的方法,就是使用 include
include ':app'
如果想指定子模塊的位置,可以使用 project 方法獲取 Project 對象,設(shè)置其 projectDir 參數(shù)
include ':app'
project(':app').projectDir = new File('./app')
rootproject/build.gradle
build.gradle 負(fù)責(zé)整體項目的一些配置,對應(yīng)的是 Project 類
gradle 構(gòu)建的時候,會根據(jù) build.gradle 生成 Project 對象,所以在 build.gradle 里寫的 dsl,其實都是 Project 接口的一些方法,Project 其實是一個接口,真正的實現(xiàn)類是 DefaultProject
其中幾個主要方法有:
- buildscript // 配置腳本的 classpath
- allprojects // 配置項目及其子項目
- respositories // 配置倉庫地址,后面的依賴都會去這里配置的地址查找
- dependencies // 配置項目的依賴
module/build.gradle
build.gradle 是子項目的配置,對應(yīng)的也是 Project 類
子項目和根項目的配置是差不多的,不過在子項目里可以看到有一個明顯的區(qū)別,就是引用了一個插件 apply plugin "com.android.application",后面的 android dsl 就是 application 插件的 extension
其中幾個主要方法有:
- compileSdkVersion // 指定編譯需要的 sdk 版本
- defaultConfig // 指定默認(rèn)的屬性,會運用到所有的 variants 上
- buildTypes // 一些編譯屬性可以在這里配置,可配置的所有屬性在 這里
- productFlavor // 配置項目的 flavor
依賴
在 gradle 3.4 里引入了新的依賴配置,如下:
| 新配置 | 棄用配置 | 行為 | 作用 |
|---|---|---|---|
| implementation | compile | 依賴項在編譯時對模塊可用,并且僅在運行時對模塊的消費者可用。 對于大型多項目構(gòu)建,使用 implementation 而不是 api/compile 可以顯著縮短構(gòu)建時間,因為它可以減少構(gòu)建系統(tǒng)需要重新編譯的項目量。 大多數(shù)應(yīng)用和測試模塊都應(yīng)使用此配置。 | api 只會暴露給直接依賴的模塊,使用此配置,在模塊修改以后,只會重新編譯直接依賴的模塊,間接依賴的模塊不需要改動 |
| api | compile | 依賴項在編譯時對模塊可用,并且在編譯時和運行時還對模塊的消費者可用。 此配置的行為類似于 compile(現(xiàn)在已棄用),一般情況下,您應(yīng)當(dāng)僅在庫模塊中使用它。 應(yīng)用模塊應(yīng)使用 implementation,除非您想要將其 API 公開給單獨的測試模塊。 | api 會暴露給間接依賴的模塊,使用此配置,在模塊修改以后,模塊的直接依賴和間接依賴的模塊都需要重新編譯 |
| compileOnly | provided | 依賴項僅在編譯時對模塊可用,并且在編譯或運行時對其消費者不可用。 此配置的行為類似于 provided(現(xiàn)在已棄用)。 | 只在編譯期間依賴模塊,打包以后運行時不會依賴,可以用來解決一些庫沖突的問題 |
| runtimeOnly | apk | 依賴項僅在運行時對模塊及其消費者可用。 此配置的行為類似于 apk(現(xiàn)在已棄用)。 | 只在運行時依賴模塊,編譯時不依賴 |
gradle wrapper
gradlew / gradlew.bat 這個文件用來下載特定版本的 gradle 然后執(zhí)行的,就不需要開發(fā)者在本地再安裝 gradle 了。這樣做有什么好處呢?開發(fā)者在本地安裝 gradle,會碰到的問題是不同項目使用不同版本的 gradle 怎么處理,用 wrapper 就很好的解決了這個問題,可以在不同項目里使用不同的 gradle 版本。gradle wrapper 一般下載在 GRADLE_CACHE/wrapper/dists 目錄下
gradle/wrapper/gradle-wrapper.properties 是一些 gradlewrapper 的配置,其中用的比較多的就是 distributionUrl,可以執(zhí)行 gradle 的下載地址和版本
gradle/wrapper/gradle-wrapper.jar 是 gradlewrapper 運行需要的依賴包
gradle 生命周期
gradle 構(gòu)建分為三個階段
初始化階段
在初始化階段,Gradle判斷需要參與編譯的工程,為每個工程創(chuàng)建一個Project對象。在這個階段,Gradle會創(chuàng)建Settings對象,并在其上執(zhí)行settings.gradle腳本,建立工程之間的層次關(guān)系。
配置階段
在這個階段,Gradle會分別在每個Project對象上執(zhí)行對應(yīng)的build.gradle腳本,對Project進行配置。
執(zhí)行階段
在執(zhí)行階段,Gradle會判斷配置階段創(chuàng)建的哪些Task需要被執(zhí)行,然后執(zhí)行選中的每個Task。
gradle 在構(gòu)建過程中,會提供一些列回調(diào)接口,方便在不同的階段做一些事情,主要的接口有下面幾個:
gradle.addBuildListener(new BuildListener() {
@Override
void buildStarted(Gradle gradle) {
println('構(gòu)建開始')
// 這個回調(diào)一般不會調(diào)用,因為我們注冊的時機太晚,注冊的時候構(gòu)建已經(jīng)開始了,是 gradle 內(nèi)部使用的
}
@Override
void settingsEvaluated(Settings settings) {
println('settings 文件解析完成')
}
@Override
void projectsLoaded(Gradle gradle) {
println('項目加載完成')
gradle.rootProject.subprojects.each { pro ->
pro.beforeEvaluate {
println("${pro.name} 項目配置之前調(diào)用")
}
pro.afterEvaluate{
println("${pro.name} 項目配置之后調(diào)用")
}
}
}
@Override
void projectsEvaluated(Gradle gradle) {
println('項目解析完成')
}
@Override
void buildFinished(BuildResult result) {
println('構(gòu)建完成')
}
})
gradle.taskGraph.whenReady {
println("task 圖構(gòu)建完成")
}
gradle.taskGraph.beforeTask {
println("每個 task 執(zhí)行前會調(diào)這個接口")
}
gradle.taskGraph.afterTask {
println("每個 task 執(zhí)行完成會調(diào)這個接口")
}
常用API
org.gradle.api.Project
Project對象是Gradle中最核心的API,通過Project對象可以訪問所有Gradle特性。
Project與build.gradle
Project對象和build.gradle文件一一對應(yīng)。在Gradle構(gòu)建時,會先創(chuàng)建Settings實例并在其上執(zhí)行settings.gradle;再通過Settings對象定義的Project層級,創(chuàng)建若干個Project實例,并分別在其上執(zhí)行對應(yīng)的build.gradle
Extra屬性
Project有一個Extra屬性,可通過ext前綴在其中定義屬性,定義好后可以不加ext前綴直接訪問。
project.ext.prop1 = "foo"
task doStuff {
ext.prop2 = "bar"
}
ext.isSnapshot = version.endsWith("-SNAPSHOT")
if (isSnapshot) {
// do snapshot stuff
}
Project的屬性/方法調(diào)用
在build.gradle中調(diào)用屬性,或調(diào)用Project.property(java.lang.String)方法時,會按順序從以下范圍查找:
- Project自身定義的屬性
- Project的Extra屬性
- 插件添加的Extension屬性
- 插件添加的Convension屬性
- Project中Task的名字
- 從父Project繼承的屬性,一直遞歸到RootProject
在build.gradle中調(diào)用方法時,會按順序從以下范圍查找:
- Project自身定義的方法
- build.gradle腳本定義的方法
- 插件添加類型為Action或Closure的Extension
- 插件添加的Convension方法
- Project中Task的名字都會創(chuàng)建一個對應(yīng)方法
- 從父Project繼承的方法,一直遞歸到RootProject
- Project中為Closure類型的屬性可以作為方法調(diào)用
Project繼承了PluginAware、ExtensionAware,分別用于支持Plugin和Extension方法。部分常用API如下。
public interface Project extends Comparable<Project>, ExtensionAware, PluginAware {
Project getRootProject();
File getRootDir();
File getBuildDir();
void allprojects(Closure configureClosure);
ScriptHandler getBuildscript();
void buildscript(Closure configureClosure);
RepositoryHandler getRepositories();
void repositories(Closure configureClosure);
ConfigurationContainer getConfigurations();
void configurations(Closure configureClosure);
DependencyHandler getDependencies();
void dependencies(Closure configureClosure);
ConfigurableFileCollection files(Object... paths);
ConfigurableFileTree fileTree(Object baseDir);
Convention getConvention();
ExtensionContainer getExtensions();
Task task(String name) throws InvalidUserDataException;
Task task(String name, Closure configureClosure);
void afterEvaluate(Closure closure);
// ...
}
常用API示例(以下腳本均寫在build.gradle中):
// 配置Gradle插件,閉包參數(shù)會在ScriptHandler上執(zhí)行
buildscript {
// ...
}
// 配置所有工程,閉包參數(shù)會分別在每個Project上執(zhí)行
allprojects {
// ...
}
// 配置使用的倉庫,閉包參數(shù)會在RepositoryHandler上執(zhí)行
repositories {
// ...
}
// 配置依賴項,閉包參數(shù)會在DependencyHandler上執(zhí)行。
// files和fileTree也是Project提供的API,
// 而project則是DependencyHandler提供的API。
dependencies {
compile files('hibernate.jar', 'libs/spring.jar')
compile fileTree('libs')
compile project(path: ':library')
// ...
}
// 在當(dāng)前Project配置完成后,閉包會被執(zhí)行
afterEvaluate {
println "Project '$name' has been evaluated!"
}
// 在RootProject配置完成后,閉包會被執(zhí)行
rootProject.afterEvaluate {
println "RootProject '$name' has been evaluated!"
}
org.gradle.api.invocation.Gradle
Gradle對象表示一次Gradle調(diào)用,通過Project.getGradle()可以獲取這個對象。在一次構(gòu)建過程中只有一個Gradle對象。
public interface Gradle extends PluginAware {
String getGradleVersion();
File getGradleUserHomeDir();
File getGradleHomeDir();
Gradle getParent();
Project getRootProject() throws IllegalStateException;
void rootProject(Action< ? super Project> action);
void allprojects(Action< ? super Project> action);
TaskExecutionGraph getTaskGraph();
StartParameter getStartParameter();
ProjectEvaluationListener addProjectEvaluationListener(ProjectEvaluationListener listener);
void removeProjectEvaluationListener(ProjectEvaluationListener listener);
void beforeProject(Closure closure);
void afterProject(Closure closure);
void buildStarted(Closure closure);
void settingsEvaluated(Closure closure);
void projectsLoaded(Closure closure);
void projectsEvaluated(Closure closure);
void buildFinished(Closure closure);
void addBuildListener(BuildListener buildListener);
public void addListener(Object listener);
public void removeListener(Object listener);
public void useLogger(Object logger);
Gradle getGradle();
}
org.gradle.api.initialization.Settings
Settings對象主要用于配置Project的層級結(jié)構(gòu)。
Settings對象和settings.gradle文件一一對應(yīng)。Gradle構(gòu)建的第一步,就是創(chuàng)建Settings對象并其上執(zhí)行settings.gradle腳本。
public interface Settings extends PluginAware {
String DEFAULT_SETTINGS_FILE = "settings.gradle";
void include(String[] projectPaths);
void includeFlat(String[] projectNames);
Settings getSettings();
File getSettingsDir();
File getRootDir();
ProjectDescriptor getRootProject();
ProjectDescriptor project(String path) throws UnknownProjectException;
ProjectDescriptor findProject(String path);
ProjectDescriptor project(File projectDir) throws UnknownProjectException;
ProjectDescriptor findProject(File projectDir);
StartParameter getStartParameter();
Gradle getGradle();
}
常用API示例:
include()可以配置包含Project,例如include ':app', ':library'-
project()可獲取ProjectDescriptor從而做一些配置,例如經(jīng)常會配置Gradle依賴本地Library工程的路徑:include ':img:library'project(':img:library').projectDir = new File('../../img/library')
org.gradle.api.Task
Task
Task也是Gradle中很重要的API。Task代表構(gòu)建過程中的一個原子操作,例如編譯classes文件或生成JavaDoc。
每個Task屬于一個Project。每個Task都有一個名字。所屬Project名+Task名可組成唯一的完整名(fully qualified path),例如:app:assemble。
Action
每個Task包含一個Action序列,并在Task執(zhí)行時按先后順序執(zhí)行。通過Task的doFirst/doLast方法可以往Action序列的頭部/末尾添加Action,支持Action或閉包(閉包會被轉(zhuǎn)換成Action對象)。
Task依賴和排序
每個Task可以依賴其他Task,執(zhí)行Task時會先執(zhí)行其依賴的Task,通過dependsOn可設(shè)置依賴。每個Task還可以設(shè)置在其他Task之前、之后執(zhí)行,一般可通過mustRunAfter設(shè)置。
例如下面的配置,執(zhí)行A時一定會先執(zhí)行B;執(zhí)行A不一定會執(zhí)行C;當(dāng)A、C都要執(zhí)行時一定先執(zhí)行C。
taskA.dependsOn(taskB)
taskA.mustRunAfter(taskC)
Task的部分常用API如下:
public interface Task extends Comparable<Task>, ExtensionAware {
String getName();
Project getProject();
TaskDependency getTaskDependencies();
Task dependsOn(Object... paths);
String getPath();
Task doFirst(Action< ? super Task> action);
Task doFirst(Closure action);
Task doLast(Action< ? super Task> action);
Task doLast(Closure action);
Task configure(Closure configureClosure);
Task mustRunAfter(Object... paths);
TaskDependency shouldRunAfter(Object... paths);
// ...
}
Task 的一些重要方法分類如下:
- Task 行為
Task.doFirst
Task.doLast - Task 依賴順序
Task.dependsOn
Task.mustRunAfter
Task.shouldRunAfter
Task.finalizedBy - Task 的分組描述
Task.group
Task.description - Task 是否可用
Task.enabled - Task 輸入輸出
gradle 會比較 task 的 inputs 和 outputs 來決定 task 是否是最新的,如果 inputs 和 outputs 沒有變化,則認(rèn)為 task 是最新的,task 就會跳過不執(zhí)行
Task.inputs
Task.outputs - Task 是否執(zhí)行
可以通過指定 Task.upToDateWhen = false 來強制 task 執(zhí)行
Task.upToDateWhen
Task創(chuàng)建
注:Gradle不推薦使用
task hello << { ... }的方式定義Task,并會在后續(xù)版本刪除,因此這里不做介紹。
在build.gradle中創(chuàng)建Task,最常見寫法如下。task(xxx)是Project提供的API,最終調(diào)用了TaskContainer的create方法。可接收參數(shù)包括:
- Task名稱(必選)
-
Map<String, ?>類型配置(可選) - 閉包配置(可選)
task hello(dependsOn: clean) {
doLast {
println 'hello'
}
}
也可以直接調(diào)用TaskContainer創(chuàng)建Task,Project中的tasks屬性即為TaskContainer對象。
tasks.create('hello')
Task創(chuàng)建后會在Project上添加一個同名方法,調(diào)用這個方法可以配置Task。
task hello
hello {
doLast {
println 'hello'
}
}
Task的type屬性,帶參數(shù)的Task
還可以用類實現(xiàn)Task,創(chuàng)建Task時指定type為這個class即可,定義Task的類通常繼承自DefaultTask。下列示例代碼中給Task定義了一個名為name的參數(shù)。
import org.gradle.api.internal.tasks.options.Option
class HelloTask extends DefaultTask {
String personName = '';
HelloTask() {
doLast {
println "Hello " + personName
}
}
@Option(description = "set person name", option = "name")
def setMessage(String name) {
this.personName = name;
}
}
task hello(type: HelloTask)
命令行中執(zhí)行效果:
$ ./gradlew hello --name Tom
:hello
Hello Tom
BUILD SUCCESSFUL
Total time: 0.889 secs