看问题角度不一样会有不同的理解,平时写项目,在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)
}
}
复制代码