Gradle 相關(guān)總結(jié)
APT 和 AGPTransform 區(qū)別
Gradle+Transform+Asm自動化注入代碼
Android 360加固+Walle多渠道自動化打包上傳蒲公英
gradle 是什么?
官方解釋:Gradle is an open-source build automation tool focused on flexibility and performance. Gradle build scripts are written using a Groovy or Kotlin DSL
使用純Java語言編寫的,基于Ant、Maven概念開源的自動化構(gòu)建工具,專注于靈活性和性能的構(gòu)建腳本,摒棄了基于XML的繁瑣配置,采用 Groovy 或 Kotlin 的 特定領(lǐng)域語言(DSL) 來編寫。
Gradle 作為一個自動化構(gòu)建工具,Gradle 是通過組織一系列 Task 來最終完成自動化構(gòu)建的,所以 Task 是 Gradle 中最重要的概念,Gradle保證這些Task按照其依賴關(guān)系的順序執(zhí)行,并且每個任務(wù)僅執(zhí)行一次,這些任務(wù)形成有向無環(huán)圖,在執(zhí)行任何Task之前,Gradle會構(gòu)建完整的依賴關(guān)系圖。比如以Android開發(fā)構(gòu)建 APK 為例,整個過程要經(jīng)過 資源的處理,javac 編譯,dex 打包,簽名等等步驟,而這一系列步驟就對應(yīng)到 Gradle 里的Task,Gradle 可以類比做一條流水線,而Task 就好比作流水線上的工人,而每個工人負(fù)責(zé)自己不同的事情,經(jīng)過每個人處理最終生成完整的構(gòu)建產(chǎn)物,如果對責(zé)任鏈模式了解就知道這個概念有點類似責(zé)任鏈模式。Gradle文檔
Gradle基礎(chǔ)
基本概念
開始學(xué)習(xí)Gradle前,我們應(yīng)該先大概了解關(guān)于Gradle中的一些基礎(chǔ)概念:
- Gradle腳本是配置腳本。在腳本執(zhí)行時,它將配置特定類型的對象。例如,在執(zhí)行構(gòu)建腳本時,它將配置類型為Project的對象。該對象稱為腳本的委托對象,比如:在腳本執(zhí)行時會為build.gradle 生成對應(yīng)的Project對象。下表展示了每種Gradle腳本的委托對象:
| Type of script | Delegates to instance of |
|---|---|
| Build script (build.gradle) | Project |
| Init script | Gradle |
| Settings script(settings.gradle) | Settings |
你可以在腳本中使用委托對象的屬性和方法。
-
每個Gradle腳本都實現(xiàn)Script接口。該接口定義了可以在腳本中使用的許多屬性和方法,如下圖:
script (2).png
script.png
1. Gradle構(gòu)建生命周期 Build Lifecycle
1.1初始化階段(Initialization):
Gradle支持單項目和多項目構(gòu)建。在初始化階段,Gradle確定將要參與構(gòu)建的項目,并為每個項目創(chuàng)建一個Project實例。
Init Script 讀取全局腳本,初始化一些全局通用屬性,如:獲取Gradle User Home 目錄(新建init.gradle腳本打印信息)、Gradle Home(新建*.gradle腳本打印信息)、Gradle 版本信息;
Settting Script 初始化一次構(gòu)建所參與的所有模塊,對應(yīng)setting.gradle腳本,組織管理項目中所有模塊的腳本,參加構(gòu)建的腳本都需要在此腳本中進行配置申明;
初始化階段主要做的事情是讀取setting.gradle腳本中配置的模塊,有哪些模塊需要參與本次的構(gòu)建,然后為對應(yīng)的模塊創(chuàng)建 Project 對象,settings.gradle 是負(fù)責(zé)配置項目的腳本對應(yīng) Settings 類,gradle 構(gòu)建過程中,會根據(jù) settings.gradle 生成 Settings 的對象;
1.2配置階段(Configuration):
開始加載所有在setting.gradle配置聲明的所有模塊Build Script即執(zhí)行build.gradle語句,根據(jù)build.gradle腳本配置對應(yīng)模塊的Project對象,并創(chuàng)建模塊中對應(yīng)Task,最終根據(jù)所有的Task生成依賴圖(DAG有向無環(huán)圖);
配置階段主要做的事情是對Initialization階段創(chuàng)建的Project對象進行配置,這時候會執(zhí)行對應(yīng)模塊的build.gradle 腳本,并且會生成要執(zhí)行的 Task;
1.3執(zhí)行階段(Execution):
*執(zhí)行階段主要做的事情就是執(zhí)行 Task,進行主要的構(gòu)建工作;
小結(jié):
在Initialization階段首先會生成一個全局唯一的Gradle對象和Settings對象,其次是根據(jù)setting.gradle腳本中的配置信息為不同模塊生成Project對象。
Configuration階段主要是配置或初始化或配置Initialization階段生成的Project對象和生成Project對應(yīng)的Task依賴圖。
在Gradle腳本中,setting.gradle、build.gradle它們是配置腳本,腳本在執(zhí)行時,實際上是生成或配置了一個特殊類型的對象,比如:Init Script → Gradle對象、Settings Script → Settings對象、Build Script → Project對象;這些對象又稱腳本的代理對象,
代理對象上的每個屬性、方法都可以在腳本中使用。每個Gradle腳本都實現(xiàn)了Script接口,由0個或多個腳本語句 (statements)和腳本塊組成。 腳本語句可以包含:函數(shù)調(diào)用、屬性賦值和本地變量定義,腳本塊則是一個方法調(diào)用,傳入一個 配置閉包,執(zhí)行時對代理對象進行配置。
Initialization(初始化階段)
1.Init Script(初始化腳本)
涉及到的腳本執(zhí)行順序如下:
GRADLE_USER_HOME/init.gradle → GRADLE_USER_HOME/init.d/*.gradle
這一步生成 Gradle 對象,并且會優(yōu)先執(zhí)行GRADLE_USER_HOME/init.gradle腳本和GRADLE_USER_HOME/init.d/*.gradle腳本,如果存在的話,下面是Gradle類部分源碼如下:
public interface Gradle extends PluginAware {
String getGradleVersion();//執(zhí)行此次構(gòu)建的Gradle版本;
File getGradleUserHomeDir();//Gradle User Home目錄;
File getGradleHomeDir();//執(zhí)行此次構(gòu)建的Gradle目錄;
Project getRootProject()// 獲取當(dāng)前構(gòu)建的根項目
StartParameter getStartParameter();//獲取傳入當(dāng)前構(gòu)建的所有參數(shù)
TaskExecutionGraph getTaskGraph();//獲取當(dāng)前構(gòu)建的task graph,此對象在taskGraph.whenReady { } 后才具有內(nèi)容
}
Gradle父類 PluginAware 源碼 :
public interface PluginAware {
PluginContainer getPlugins();
void apply(Closure closure);
void apply(Action<? super ObjectConfigurationAction> action);
void apply(Map<String, ?> options);
PluginManager getPluginManager();
}
2. Settings Script(設(shè)置腳本)
settings.gradle腳本文件由Gradle通過命名約定確定。該文件的默認(rèn)名稱是settings.gradle,settings.gradle腳本文件在初始化階段執(zhí)行。多項目構(gòu)建必須在多項目層次結(jié)構(gòu)的根項目中具有settings.gradle文件。這是必需的,因為settings.gradle腳本文件定義了哪些項目正在參與多項目構(gòu)建。對于單項目構(gòu)建,設(shè)置文件是可選的。
-
settings.gradle負(fù)責(zé)配置聲明參與構(gòu)建過程的模塊,對應(yīng) Settings 類,Gradle 構(gòu)建過程中,Gradle會根據(jù) settings.gradle腳本生成 Settings 的對象。Settings部分源碼:
public interface Settings extends PluginAware, ExtensionAware { String DEFAULT_SETTINGS_FILE = "settings.gradle"; void include(String... projectPaths); void includeFlat(String... projectNames); ProjectDescriptor project(File projectDir) throws UnknownProjectException; } Settings類中,比較常用的方法就是 include(String… projectPaths) 方法,用于添加參與構(gòu)建的模塊,ProjectDescriptor project(File projectDir) 方法也是比較常用,當(dāng)子模塊不和settings.gradle腳本同一個目錄時需使用相對路徑描述,比如: project(':user').projectDir = File(rootDir, "user-module"),user模塊的目錄只向根目錄先的user-module目錄。
-
前面有提到過Settings類實現(xiàn)PluginAware(插件相關(guān)接口),所以可以通過 Settings.pluginManagement方法的配置插件構(gòu)建過程中需要的插件倉庫:
pluginManagement { // 對應(yīng)PluginManagementSpec類 repositories { // 管理Plugin Repository(倉庫) google { url "https://maven.aliyun.com/repository/gradle-plugin" } } resolutionStrategy { eachPlugin { // 替換模塊 if (requested.id.namespace == "com.github.plugin") { useModule("com.xxxxxx.plugin:${requested.version}") } // 統(tǒng)一插件版本 if (requested.id.id == "com.android.tools.build") { useVersion("4.1.2") } } } }
配置階段(Configuration)
1.Build Script(構(gòu)建腳本build.gradle腳本)
build.gradle用于配置模塊的構(gòu)建所需要信息,分為根目錄的 Root Build Script 和 子模塊的 Module Build Script。
1.1、Root Build Script
- 根目錄的 Root Build Script ,一般是對所有子模塊進行全局的統(tǒng)一的配置,build.gradle 腳本負(fù)責(zé)整體項目的基本配置,對應(yīng)的是Project類,我們稱之為:rootProject;
- Gradle 在構(gòu)建的時候,會根據(jù)根目錄下的build.gradle腳本生成 Project 對象,所以在 build.gradle 里寫的 dsl,其實都是 Project 接口的一些方法,Project具體的實現(xiàn)類是DefaultProject類。
1.2、Module Build Script
- 子模塊的 Module Build Script,對當(dāng)前子模塊進行配置,同樣的對應(yīng)的是Project類,我們稱之為:childProject;子項目和根項目的配置基本是相似,不過在子項目里有一個明顯的區(qū)別就是引用
apply plugin "com.android.application",而androiddsl 就是 application 插件的 extension,關(guān)于 android plugin dsl 可以看 android-gradle-dsl;
1.2.1 插件引入
Gradle插件是什么,官方解釋:
Gradle at its core intentionally provides very little for real world automation. All of the useful features, like the ability to compile Java code, are added by plugins. Plugins add new tasks (e.g. JavaCompile), domain objects (e.g. SourceSet), conventions (e.g. Java source is located at
src/main/java) as well as extending core objects and objects from other plugins.
- 簡單點說就是,Gradle自身并沒有提供多少真實可用的功能,它只是一個負(fù)責(zé)定義流程和規(guī)則的框架,具體的功能都是由插件來完成的,比如:編譯Java 則引用用
java插件,編譯Kotlin用kotlin插件,而這些插件并不是Gradle中自帶的功能,而是有第三方作者定義的插件,所以說Gradle只負(fù)責(zé)定義流程和規(guī)則的框架。
1.2.1 .1 插件類型
Gradle中有兩種通用的插件類型,即二進制插件和腳本插件。
- 二進制插件: 通過實現(xiàn)Plugin接口以編程的方式編寫;
- 腳本插件:使用Gradle DSL語言以聲明方式編寫,通常存在于另一個腳本文件中的一段腳本代碼;
示例:
引用二進制插件:
apply plugin: 'com.android.application'
應(yīng)用工程目錄下的腳本插件:
apply from: rootProject.file("script_plugin.gradle")
1.2.2 插件屬性配置和方法調(diào)用
如果項目中應(yīng)用了某個插件,就可以使用此插件提供的DSL進行配置,以此干預(yù)模塊的構(gòu)建過程。以com.android.application插件構(gòu)建為例:
// 引入android.application插件
apply plugin: 'com.android.application'
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.github.gradle"
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
'proguard-rules.pro'
}
}
sourceSets {
main {}
}
}
關(guān)于自定義插件請移步Gradle+Transform+Asm自動化注入代碼
Gradle 相關(guān)API介紹
由于Gradle類的方法屬性很多,Gradle API文檔,我就挑幾個平時可能會常用的的方法說一說:
| API | 描述 |
|---|---|
| void beforeProject(Closure closure) | 當(dāng)每個Project對象配置之前被調(diào)用,包括根Project |
| void afterProject(Closure closure) | 當(dāng)每個Project對象配置完畢之后被調(diào)用,包括根Project |
| buildStarted(Closure closure) | 當(dāng)構(gòu)建開始時回調(diào),這個一般是看不到打印的 |
| settingsEvaluated(Closure closure) | 當(dāng)Settings對象配置完成時回調(diào) |
| projectsLoaded | 在初始化階段中所有的Project對象創(chuàng)建完成時回調(diào) |
| projectsEvaluated | 當(dāng)所有的Project配置完成時回調(diào) |
| buildFinished | 當(dāng)構(gòu)建完成時回調(diào) |
| addBuildListener | 添加構(gòu)建監(jiān)聽 |
| getRootProject | 返回Root Project |
| TaskExecutionGraph getTaskGraph() | 返回此次構(gòu)建TaskExecutionGraph,此對象在taskGraph.whenReady { } 后才具有內(nèi)容,因為task還沒填充完畢。 |
| String getGradleVersion() | 執(zhí)行此次構(gòu)建的Gradle版本 |
| File getGradleUserHomeDir() | Gradle User Home目錄 |
| File getGradleHomeDir() | 執(zhí)行此次構(gòu)建的Gradle目錄 |
| Project getRootProject() | 獲取當(dāng)前構(gòu)建的根項目 |
| StartParameter getStartParameter() | 獲取傳入當(dāng)前構(gòu)建的所有參數(shù) |
TaskExecutionGraph 相關(guān)API介紹
TaskExecutionGraph負(fù)責(zé)管理作為構(gòu)建部分的Task實例的執(zhí)行。 TaskExecutionGraph維護要執(zhí)行(或已執(zhí)行)的任務(wù)的執(zhí)行計劃,您可以從構(gòu)建文件中查詢該計劃。 您可以通過調(diào)用Gradle.getTaskGraph()訪問TaskExecutionGraph。在構(gòu)建文件中,您可以使用gradle.taskGraph對其進行訪問。 僅在配置了此次構(gòu)建中的所有Project之后,才填充TaskExecutionGraph。在此之前是空的。當(dāng)填充Graph時,可以使用whenReady或addTaskExecutionGraphListener方法接收通知。
| API | 描述 |
|---|---|
| addTaskExecutionGraphListener | 向TaskExecutionGraph添加TaskExecutionGraphListener監(jiān)聽,在填充TaskExecutionGraph之后以及執(zhí)行任何任務(wù)之前調(diào)用此方法 |
| whenReady(Action<TaskExecutionGraph> action) | 當(dāng)TaskExecutionGraph填充完成時被調(diào)用 |
| addTaskExecutionListener | 向TaskExecutionGraph添加TaskExecutionListener監(jiān)聽 |
| beforeTask(Action<Task> action) | 當(dāng)TaskExecutionGraph管理的某個任務(wù)執(zhí)行之前被調(diào)用 |
| afterTask(Action<Task> action) | 當(dāng)TaskExecutionGraph管理的某個任務(wù)執(zhí)行完成之后被調(diào)用 |
| hasTask(Task task) | 查詢TaskExecutionGraph是否存在該task |
| getAllTasks | 獲取TaskExecutionGraph管理的所有的Task |
| Set getDependencies(Task task) | 返回TaskExecutionGraph管理的與參數(shù)Task的依賴關(guān)系 |
Project相關(guān)API介紹
該接口是用于與構(gòu)建文件中的Gradle對象交互的主要API。可以通過Projec對象以編程方式訪問Gradle的所有功能。
Project本質(zhì)上是Task對象的集合。
Project生命周期Lifecycle
Project對象和"build.gradle"文件之間存在一對一的關(guān)系。在構(gòu)建初始化期間,Gradle 為要參與構(gòu)建的每個模塊創(chuàng)建一個Project對象,如下所示:
- 為構(gòu)建創(chuàng)建一個Settings實例。
- 根據(jù)"settings.gradle"腳本(如果存在)配置Setttings對象。
- 使用已配置的Settings對象創(chuàng)建Project實例的層次結(jié)構(gòu)。
- 通過執(zhí)行"build.gradle"文件(如果存在)來配置對應(yīng)的Project對象。這些Project對象以廣度順序進行配置,因此在其子項目之前先對其進行配置??梢酝ㄟ^調(diào)用EvaluationDependsOnChildren()或通過使用EvaluationDependsOn(String)添加顯式配置依賴項來覆蓋此順序。
這里僅僅只介紹常用的API,詳細(xì)API請看-官方文檔
| API | 描述 |
|---|---|
| void beforeEvaluate(Action <? super Project> action) | 在配置該Project之前立即調(diào)用方法。 |
| void afterEvaluate(Action <? super Project> action) | 在配置該Project之后立即調(diào)用方法。 |
| getRootProject() | 獲取根Project |
| getRootDir | 返回該Project根目錄。根目錄是根Project的Project目錄。 |
| getBuildDir | 返回該項目的構(gòu)建目錄,構(gòu)建目錄是構(gòu)建過程中build產(chǎn)物都放入到這個里面,構(gòu)建目錄的默認(rèn)值為projectDir / build。 |
| setBuildDir(File path) | 設(shè)置該Project的構(gòu)建目錄。構(gòu)建目錄是構(gòu)建過程中build產(chǎn)物都放入到這個里面。 |
| getParent() | 獲取此Project的父Project |
| getChildProjects | 獲取此Project的所有直接子Project |
| setProperty(String name, Object value) | 為Project的屬性設(shè)置新值 |
| getProject() | 返回當(dāng)前Project對象,可用于訪問當(dāng)前Project的屬性和方法 |
| getAllprojects | 返回當(dāng)前Project,以及子Project的集合 |
| allprojects(Closure configureClosure) | 在配置階段,閉包中返回Project及其子Project。 |
| getSubprojects | 返回當(dāng)前Project下的所有子Project |
| subprojects(Closure configureClosure) | 返回當(dāng)前Project下的所有子Project到閉包中 |
| Task task(String name) | 創(chuàng)建一個Task,添加到此Project |
| getAllTasks(boolean recursive) | 如果recursive為true那么返回當(dāng)前Project和子Project的全部Task,如果為false只返回當(dāng)前Project的所有task |
| getTasksByName(String name, boolean recursive) | 根據(jù)名字返回Task,如果recursive為true那么返回當(dāng)前Project和子Project的Task,如果為false只返回當(dāng)前Project的task |
| hasProperty(String propertyName) | 查看是否存在此屬性 |
| getProperties() | 獲取所有屬性 |
| dependencies(Closure configureClosure) | 為Project配置依賴項 |
| buildscript(Closure configureClosure) | 為該Project配置構(gòu)建腳本classpath。 |
| getTasks() | 返回此Project中所有的tasks |
| WorkResult copy(Action<? super CopySpec> action) | 復(fù)制指定的文件. |
| CopySpec copySpec(Action<? super CopySpec> action) | 創(chuàng)建一個CopySpec,以后可以將其用于復(fù)制文件或創(chuàng)建存檔。給定的操作用于在此方法返回CopySpec之前對其進行配置。 |
| delete<? super DeleteSpec> action) | 刪除指定的文件。 |
| Task task(String name, Closure configureClosure) | 使用給定名稱創(chuàng)建一個Task并將其添加到該Project中。 |
| Task task(Map<String,?) args,String name) | 使用給定名稱創(chuàng)建一個Task并將其添加到該項目中??梢詫?chuàng)建參數(shù)的Map傳遞給此方法,以控制Task的創(chuàng)建方式 |
Task介紹
Project本質(zhì)上是Task對象的集合。每個任務(wù)執(zhí)行一些基本工作,例如編譯類,運行單元測試或壓縮WAR文件。可以使用TaskContainer的create方法將任務(wù)添加到Project中。還可以使用TaskContainer上的查找方法,例如:TaskCollection.getByName(String)方法查找現(xiàn)有Task。
每個Task都會附屬一個Project,可以在TaskContainer上使用各種方法來創(chuàng)建和查找任務(wù)實例。例如,TaskContainer.create(String)使用給定名稱創(chuàng)建一個空任務(wù)。您還可以在構(gòu)建文件中使用task關(guān)鍵字:
task myTask
task myTask1 { Task task ->}
task myTask2(type: JavaCompile)
task myTask3(type: JavaCompile) { Task task -> }
-
Task由一系列Action對象組成。執(zhí)行Task時,通過調(diào)用Action.execute(T)依次執(zhí)行每個Action。通過調(diào)用doFirst(Action)或doLast(Action)將操作添加到任務(wù)中。
task myTaskAction {Task task-> task.doFirst(new Action<Task>() { @Override void execute(Task taskAction) { println("execute task>>>>>>>>>>>>>>>>操作") } }) } -
Groovy閉包也可以用于提供Task 的Action。當(dāng)執(zhí)行Action時,以Task做為參數(shù)調(diào)用閉包。通過調(diào)用doFirst(Closure)或doLast(Closure)將Action閉包添加到任務(wù)中。
task myTaskClosure { Task task-> task.doFirst{ } task.doLast { println("execute task closure>>>>>>>>>>>>>>>>操作") } } 一個Task可能依賴于其他Task,或者可能被安排為始終在另一個Task之后運行。任務(wù)的依賴關(guān)系使用dependsOn(Object ...)或setDependsOn(Iterable)進行控制。
task myTaskDepends { Task task ->
task.doLast {
println("execute task: " + task.getName())
}
}
task myTaskDepends1 { Task task ->
task.dependsOn("myTaskDepends")
task.doLast {
println("execute task: " + task.getName())
}
}
task myTaskDepends2 { Task task ->
task.setDependsOn([myTaskDepends,"myTaskDepends1"])
task.doLast {
println("execute task: " + task.getName())
}
}
特點:
- 一個Task可能依賴于其他Task,或者可能被安排為始終在另一個Task之后運行,如:taskA dependsOn taskB ,那么當(dāng)執(zhí)行taskA 就必須會優(yōu)先執(zhí)行taskB,不管是否同時執(zhí)行。但是如果只執(zhí)行taskB,那么taskA并不會執(zhí)行。
task排序
在某些情況下, 我們希望能控制Task的執(zhí)行順序, 這種控制并不是像上面顯式地加入(dependsOn(Object ...)或setDependsOn(Iterable)進行控制)依賴關(guān)系。 最主要的區(qū)別是我們設(shè)定的排序規(guī)則不會影響那些要被執(zhí)行的任務(wù), 只是影響Task執(zhí)行的順序本身.
task myTaskX { Task task ->
task.doLast {
println("execute task: " + task.getName())
}
}
task myTaskY { Task task ->
task.doLast {
println("execute task: " + task.getName())
}
}
myTaskY.mustRunAfter(myTaskX)
gradlew myTaskY myTaskX -q 的輸出:
execute task: myTaskX
execute task: myTaskY
注意 B.mustRunAfter(A) 或者 B.shouldRunAfter(A) 并不影響任何Task間的執(zhí)行。tasks A 和 taskB可以被獨立的執(zhí)行。排序規(guī)則只有當(dāng) 2 個任務(wù)同時執(zhí)行時才會生效。

