看問題角度不一樣會有不同的理解,平時寫項目,在build.gradle 添加一些配置,寫多了配置,就會理所固然覺得Gradle就是一個配置文件,而在學習 Gradle 以前,須要明確一點,要深刻學習Gradle 說白了咱們須要把他當作是一個編程框架,而咱們須要瞭解的就是它的 API,並利用這些 API 完成一些任務。html
Initialization phase(初始化):這個階段執行 settings.gradle文件,解析本項目包含多少個 projectjava
Configration階段的目標是解析每一個project中的build.gradle。解析每一個子目錄中的 build.gradle,分別是加載插件,加載依賴,加載 Task 和執行腳本android
Execution phase(執行):這個階段就是執行任務git
生命週期官方文檔描述github
Gradle是 groovy 語言編寫的,而 groovy 又基於Java,因此 Gradle 在執行 groovy 腳本的時候實際上是將其解析轉換成 Java 對象,而這種對象有三種基本類型編程
Gradle 對象:當咱們執行 gradle xxx 命令的時候,gradle 會從默認的配置腳本中構造出一個 Gradle 對象。在整個執行過程當中,只有這麼一個對象。Gradle 對象的數據類型就是 Gradle。api
Project 對象: 每個 build.gradle 會轉換成一個 Project 對象markdown
Settings 對象: 每個 settings.gradle 都會轉換成一個 Settings 對象閉包
每一個Gradle腳本都實現該Script接口。該接口定義了能夠在腳本中使用的許多屬性和方法app
由上一小節的Gradle編程模型中,每個 build.gradle 文件都會轉換成一個 Project 對象,Project和 build.gradle 文件之間存在一對一的關係。在 Gradle 術語中,Project 對象對 應的是 Build Script
每個Project 項目包含不少個Task,Task就是對應插件,也能夠這樣說一個 Project中有多少個 Task 實際上是由插件的多少來決定的
,因此在build.gradle中,須要加載插件,加載依賴,設置屬性
//若是 project 是編譯 Android APP,,則加載此插件
apply plugin: 'com.android.application'
//若是 project 是編譯 Library,則加載此插件
apply plugin: 'com.android.library'
複製代碼
注意:build.gradle 中 apply 其餘 gradle 文件須要在同一個目錄下,不然須要文件路徑 + 文件名格式來 apply
apply from: 'config.gradle'
//文件路徑 + 文件名
apply from: rootProject.getRootDir().absolutePath+'/config.gradle'
複製代碼
下文中有提到閉包,閉包,英文叫 Closure,是 Groovy 中很是重要的一個數據類型或者說一種概念;==它表明了一段可執行的代碼==
//除了 ext.xxx=value 這種定義方法外,還可使用 ext{}這種書寫方法
ext {
//閉包
android = [
compileSdkVersion : 28,
buildToolsVersion : "28.0.0",
minSdkVersion : 21,
targetSdkVersion : 28,
versionCode : 7,
versionName : "1.0.6",
renderscriptTargetApi : 21
]
version = [
supportLibraryVersion : "28.0.0",
smartrefreshVersion : "1.1.0-alpha-25",
okhttpVersion : "3.12.0",
retrofitVersion : "2.3.0",
glideVersion : "4.8.0",
daggerVersion : "2.22.1",
butterknifeVersion : "8.8.1",
fragmentationVersion : "1.3.6",
]
dependencies = [
//base
"appcompat-v7" : "com.android.support:appcompat-v7:${version["supportLibraryVersion"]}",
"cardview-v7" : "com.android.support:cardview-v7:${version["supportLibraryVersion"]}",
"support-v4" : "com.android.support:support-v4:${version["supportLibraryVersion"]}",
"design" : "com.android.support:design:${version["supportLibraryVersion"]}",
"recyclerview" : "com.android.support:recyclerview-v7:${version["supportLibraryVersion"]}",
"constraint-layout" : "com.android.support.constraint:constraint-layout:1.1.3",
.......
]
}
複製代碼
dependencies {
implementation rootProject.ext.dependencies["appcompat-v7"]
}
複製代碼
首先照例貼出Task的Api文檔地址
Task 是 Gradle 中的一種數據類型,它表明了一些要執行或者要乾的工做。不一樣的插件
能夠添加不一樣的 Task。每個 Task 都須要和一個 Project 關聯
// Task 是和 Project 關聯的,因此能夠利用 Project 的 task 函數來建立一個 Task
task myTask
task myTask {
//閉包配置
configure closure
}
//eg:
project.task("hello1"){
doLast {
println("Hello from the GreetingPlugin")
}
}
Task myType << { task action } //注意,<<符號 是 doLast 的縮寫
//Task 建立的時候能夠指定 Type
task myTask(type: SomeType)
//eg:
task myTask(type:Copy) //建立的 Task 就是一個 Copy Task
task myTask(type: SomeType) {
//閉包配置
configure closure
}
複製代碼
一個 Task 能夠有若干個 Action,每一個Task 都有doFirst 和 doLast 兩個函數,用於 添加須要最早執行的 Action 和須要和須要最後執行的 Action。Action 就是一個closure(閉包)。
使用 task myTask { xxx}的時候,括號是一個 closure(閉包)。 gradle 在建立這個 Task 以後,返回給用戶以前,會先執行 closure 的邏輯任務
Task myType << { task action },能夠指定 Type,經過 type:名字表達。Gradle 本 身提供了一些通用的 Task,最多見的有 Copy 任務。Copy 是 Gradle 中的一個類。 當咱們:task myTask(type:Copy)的時候,建立的 Task 就是一個 Copy Task。
//文檔複製任務
task copyDocs(type: Copy) {
from 'src/main/doc' //從src/main/doc目錄
into 'build/target/doc' //複製到build/target/doc目錄
}
//更多例子能夠查看官方文檔
複製代碼
前面只是對 Task 一些概念進行了解,更多細節還需自行查看官方文檔。
不少第三方框架,都須要依賴自定義插件,好比阿里路由框架 ARouter,而它的實現中就有包含 Transform 的使用,瞭解並使用Transform 是讀懂第三方框架的基礎。
首先能夠看看官方文檔,從1.5.0-beta1開始,Gradle插件包含一個Transform API,容許第三方插件在將已編譯的類文件轉換爲dex文件以前對其進行操做。
Transform 說白了也是一個Task,平時在 Android 編譯項目,項目代碼會先經過 compileJava 這個task 將項目源碼編譯成 .class文件,而 Transform 則能夠接收這些編譯產生的Class文件,而且 Transform 會在 compileJava 這個task 以後執行,這樣就表示能夠在 Android 項目生成 dex 以前作一些自定義操做。
Transform 依賴引入
implementation 'com.android.tools.build:gradle:4.0.0'
複製代碼
@SuppressWarnings("MethodMayBeStatic")
public abstract class Transform {
@NonNull
public abstract String getName();
@NonNull
public abstract Set<ContentType> getInputTypes();
@NonNull
public abstract Set<? super Scope> getScopes();
/**
* Returns whether the Transform can perform incremental work.
* 是否支持增量編譯
* <p>If it does, then the TransformInput may contain a list of changed/removed/added files, unless
* something else triggers a non incremental run.
*/
public abstract boolean isIncremental();
.....
}
複製代碼
/**
* The type of of the content.
*/
enum DefaultContentType implements ContentType {
/**
* .class 文件
*/
CLASSES(0x01),
/**標準Java資源 */
RESOURCES(0x02);
private final int value;
DefaultContentType(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
}
複製代碼
enum Scope implements ScopeType {
/** 只檢索項目內容 */
PROJECT(0x01),
/** 只檢索子項目內容 */
SUB_PROJECTS(0x04),
/**只有外部庫 */
EXTERNAL_LIBRARIES(0x10),
/** 由當前變量測試的代碼,包括依賴項 */
TESTED_CODE(0x20),
/** 僅提供的本地或遠程依賴項 */
PROVIDED_ONLY(0x40),
......
}
複製代碼
public static final Set<ScopeType> PROJECT_ONLY = ImmutableSet.of(Scope.PROJECT);
public static final Set<ScopeType> SCOPE_FULL_PROJECT =
ImmutableSet.of(Scope.PROJECT, Scope.SUB_PROJECTS, Scope.EXTERNAL_LIBRARIES);
public static final Set<ScopeType> SCOPE_FULL_WITH_FEATURES =
new ImmutableSet.Builder<ScopeType>()
.addAll(SCOPE_FULL_PROJECT)
.add(InternalScope.FEATURES)
.build();
public static final Set<ScopeType> SCOPE_FEATURES = ImmutableSet.of(InternalScope.FEATURES);
public static final Set<ScopeType> SCOPE_FULL_LIBRARY_WITH_LOCAL_JARS =
ImmutableSet.of(Scope.PROJECT, InternalScope.LOCAL_DEPS);
public static final Set<ScopeType> SCOPE_FULL_PROJECT_WITH_LOCAL_JARS =
new ImmutableSet.Builder<ScopeType>()
.addAll(SCOPE_FULL_PROJECT)
.add(InternalScope.LOCAL_DEPS)
.build();
複製代碼
override fun transform(transformInvocation: TransformInvocation) {
}
複製代碼
public interface TransformInvocation {
/**
* Returns the inputs/outputs of the transform.
* @return the inputs/outputs of the transform.
*/
@NonNull
Collection<TransformInput> getInputs();
/**
* Returns the output provider allowing to create content.
* @return he output provider allowing to create content.
*/
@Nullable
TransformOutputProvider getOutputProvider();
.....
}
複製代碼
/**
* The input to a Transform.
* <p>
* It is mostly composed of a list of {@link JarInput} and a list of {@link DirectoryInput}.
*/
public interface TransformInput {
/**
* Returns a collection of {@link JarInput}.
*/
@NonNull
Collection<JarInput> getJarInputs();
/**
* Returns a collection of {@link DirectoryInput}.
*/
@NonNull
Collection<DirectoryInput> getDirectoryInputs();
}
複製代碼
經過接口 TransformInput 的定義能夠知道 transform 方法能夠出來到兩種輸入類型的文件,一直是 jar 包的集合jarInputs,另外一種是文件目錄集合 directoryInputs
以下一個例子就是分別打印 輸入的 jar 包和 .class 文件名稱
override fun transform(transformInvocation: TransformInvocation) {
println("transform 方法調用")
//獲取 輸入 文件集合
val transformInputs = transformInvocation.inputs
transformInputs.forEach { transformInput ->
// jar 文件處理
transformInput.jarInputs.forEach { jarInput ->
val file = jarInput.file
println("find jar input: " + file.name)
}
//源碼文件處理
//directoryInputs表明着以源碼方式參與項目編譯的全部目錄結構及其目錄下的源碼文件
transformInput.directoryInputs.forEach { directoryInput ->
//遍歷全部文件和文件夾 找到 class 結尾文件
directoryInput.file.walkTopDown()
.filter { it.isFile }
.filter { it.extension == "class" }
.forEach { file ->
println("find class file:${file.name}")
}
}
}
}
複製代碼
/**
* @Description: kotlin 代碼編寫自定義插件
* @author maoqitian
* @date 2020/11/13 0013 17:01
*/
class MainPlugin :Plugin<Project> {
override fun apply(project: Project) {
println("======自定義MainPlugin加載===========")
//註冊執行自定義的 Transform task
val asmTransform = project.extensions.getByType(AppExtension::class.java)
println("=======registerTransform ASMLifecycleTransform ==========")
val transform = ASMLifecycleTransform()
asmTransform.registerTransform(transform)
}
}
複製代碼