简单了解下 groovy 语法,讲一下 groovy 里面的 owner 和 delegate 机制
groovy语法部分和java基本是类似的,特别也是闭包部分,这部分主要也和groovy的闭包相关
闭包
**Groovy中的闭包是一个开放,匿名的代码块,可以接受参数,返回值并分配给变量,并且是一个对象,
闭包的写法
//执行一句话
{ printf 'Hello World' }
//闭包有默认参数it,且不用申明
{ println it }
//闭包有默认参数it,申明了也无所谓
{ it -> println it }
// name是自定义的参数名
{ name -> println name }
//多个参数的闭包
{ String x, int y ->
println "hey ${x} the value is ${y}"
}
groovy.lang.Closure对象
其实,每定义的闭包是一个Closure对象,我们可以把一个闭包赋值给一个变量
def innerClosure = {
printf("hello")
}
def hello = { String x ->
printf("hello ${x}")
}
还可以把闭包作为方法的参数类型
void setOnClickListener(Closure closure) {
this.onClickListener = closure
}
执行闭包对象
//执行innerClosure 闭包
innerClosure ()
//or
innerClosure.call()
//带参数的闭包
hello("world")
//or
hello.call("world")
闭包内this,owner,delegate对象
在闭包内部,有三个内置对象this,owner,delegate,我们可以直接this,owner,delegate调用,或者用get方法:
- getThisObject() 等于 this
- getOwner() 等于 owner
getDelegate() 等于delegate
this 对应于定义闭包的那个类,如果在内部类中定义,指向的是内部类
- owenr 对应定义声明闭包的封闭对象,可以是类或者闭包。它与 this 有点类似,最大的不同是 owner 指向的是直接包裹它的对象(普通对象或者闭包对象)
- delegate 默认是和owner一致,或者自定义delegate指向,我们可以通过 delegate 关键字和
getDelegate()
、setDelegate(x)
方法来使用委托对象
下面通过代码演示:
public class OuterClass {
class InnerClass {
def outerClosure = {
def innerClosure = {
}
printfMsg("innerClosure", innerClosure)
println("------")
printfMsg("outerClosure", outerClosure)
}
void printfMsg(String flag, Closure closure) {
def thisObject = closure.getThisObject()
def ownerObject = closure.getOwner()
def delegate = closure.getDelegate()
println("${flag} this: ${thisObject.toString()}")
println("${flag} owner: ${ownerObject.toString()}")
println("${flag} delegate: ${delegate.toString()}")
}
}
def callInnerMethod() {
def innerClass = new InnerClass()
innerClass.outerClosure.call()
println("------")
println("outerClosure toString ${innerClass.outerClosure.toString()}")
}
static void main(String[] args) {
OuterClass outer = new OuterClass();
outer.callInnerMethod()
}
}
输出结果如下:
innerClosure this: OuterClass$InnerClass@2d1dee39
innerClosure owner: OuterClass$InnerClass$_closure1@2b0f373b
innerClosure delegate: OuterClass$InnerClass$_closure1@2b0f373b
------
outerClosure this: OuterClass$InnerClass@2d1dee39
outerClosure owner: OuterClass$InnerClass@2d1dee39
outerClosure delegate: OuterClass$InnerClass@2d1dee39
------
outerClosure toString OuterClass$InnerClass$_closure1@2b0f373b
分析结果:
innerClosure
- this:对应于定义闭包的那个类,如果在内部类中定义,指向的是内部类,结果是OuterClass$InnerClass对象
- owner:结果是OuterClass$InnerClass$_closure1对象 ,即outerClosure
- delegate:同owenr
outerClosure
- this:结果是OuterClass$InnerClass对象
- owner:结果是OuterClass$InnerClass对象
- delegate:同owenr
delegate
设置delegate的意义就是讲闭包和一个具体的对象关联起来,默认情况下 delegate 被设置成 owner
比如如下代码:
class Main {
def cc = {
name = "lisi"
age = 26
code("C++")
}
static void main(String... args) {
Main main = new Main()
Person person = new Person(name: "zhangsan", age: 14)
println person.toString()
main.cc.delegate = person
main.cc.call()
println person.toString()
}
}
class Person {
String name
int age
void code(String str) {
println(name+"精通${str}技能")
}
@Override
String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}'
}
}
在设置delegate的时候,进行一个关联
输入结果为 :
Person{name='zhangsan', age=14}
lisi精通C++技能
Person{name='lisi', age=26}
这样闭包中的执行会去执行delegate对象中的值或者函数。
如果闭包所在的类有相同的函数和变量,他会如何执行呢,代码如下:
class Main {
String name
int age
void code(String str){
println "不精通${str}"
}
def cc = {
name = "lisi"
age = 26
code("C++")
}
static void main(String... args) {
//和上面代码一样
}
}
在Main 这个class中我们加入了与闭包相同的变量和函数,可以看到输出结果为:
Person{name='zhangsan', age=14}
不精通C++
Person{name='zhangsan', age=14}
两个结果出现了不一致,这是因为涉及到groovy的委托策略,我们可以通过setResolveStrategy进行设置
Groovy 闭包的委托策略有如下几种:
Closure.OWNER_FIRST
:默认策略,首先从 owner 上寻找属性或方法,找不到则在 delegate 上寻找。Closure.DELEGATE_FIRST
:和上面相反,首先从 delegate 上寻找属性或者方法,找不到则在 owner 上寻找。Closure.OWNER_ONLY
:只在 owner 上寻找,delegate 被忽略。Closure.DELEGATE_ONLY
:和上面相反,只在 delegate 上寻找,owner 被忽略。Closure.TO_SELF
:高级选项,让开发者自定义策略,必须要自定义实现一个 Closure 类,一般用不到。
因此默认是OWNER_FIRST策略,所以会优先从owner上寻找,而上面代码闭包的owner为Main这个class,会优先去Main找对应的变量和函数
static void main(String... args) {
Main main = new Main()
Person person = new Person(name: "zhangsan", age: 14)
println person.toString()
main.cc.delegate = person
main.cc.setResolveStrategy(Closure.DELEGATE_FIRST)
// main.cc.setResolveStrategy(Closure.OWNER_FIRST)
main.cc.call()
println person.toString()
}
如果设置为Closure.DELEGATE_FIRST,结果就会一致。
PS:android的gradle配置文件中会看到如下代码:
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0"
}
}
简单描述下 Gradle 任务的生命周期
下面是gradle构建的生命周期,task的生命周期最后总结一下
初始化阶段
初始化阶段主要做的事情是有哪些项目需要被构建,然后为对应的项目创建 Project 对象
在初始化阶段,Gradle 会解析工程根目录下的setting.gradle
文件,并构造一个 Settings 实例。Settings 是一个接口,主要包含以下方法:
public interface Settings extends PluginAware, ExtensionAware {
String DEFAULT_SETTINGS_FILE = "settings.gradle";
void include(String... var1);
void includeFlat(String... var1);
File getSettingsDir();
File getRootDir();
Gradle getGradle();
...
}
include 方法指定了参与构建的项目(build.gradle)。在初始化阶段,Gradle 会为「包含」的每个的项目实例化一个 Project 实例。
在 setting.gradle 里,可以对构建过程中的节点增加监听,监听器添加在Setting.getGradle()
实例上:
include ':app'
gradle.addBuildListener(new BuildListener() {
@Override
void buildStarted(Gradle gradle) {
println('构建开始')
// 这个回调一般不会调用,因为我们注册的时机太晚,注册的时候构建已经开始了,是 gradle 内部使用的
}
@Override
void settingsEvaluated(Settings settings) {
println('settings 文件解析完成')
}
@Override
void projectsLoaded(Gradle gradle) {
println('项目加载完成')
gradle.rootProject.subprojects.each { pro ->
pro.beforeEvaluate {
println("${pro.name} 项目配置之前调用")
}
pro.afterEvaluate{
println("${pro.name} 项目配置之后调用")
}
}
}
@Override
void projectsEvaluated(Gradle gradle) {
println('项目解析完成')
}
@Override
void buildFinished(BuildResult result) {
println('构建完成')
}
})
gradle.taskGraph.whenReady {
println("task 图构建完成")
}
gradle.taskGraph.beforeTask {
println("每个 task 执行前会调这个接口")
}
gradle.taskGraph.afterTask {
println("每个 task 执行完成会调这个接口")
}
除了设置监听器方法,还可以直接设置闭包,例如:
gradle.projectsLoaded {
println '【初始化阶段结束】'
}
配置阶段
配置阶段主要做的事情是对上一步创建的项目进行配置,Gradle 会解析每个项目中的 build.gradle 文件,完成「Project 配置」&「Task 配置」,并根据 Task 依赖关系来创建一个有向无环图。
apply plugin: 'com.android.application'
println '配置 app project'
task hello {
println '配置 hello task'
doFirst{
println 'hello 动作'
}
}
...
构建过程输出如下:
Executing tasks: [:app:assembleDebug] in project E:\workspace\...
【settings.gradle 解析完成】
【初始化阶段结束】
> Configure project :app
配置 app project
配置 hello task
【配置阶段结束】
> Task :app:preBuild UP-TO-DATE
> Task :app:preDebugBuild UP-TO-DATE
> Task :app:checkDebugManifest UP-TO-DATE
> ...
> Task :app:assembleDebug
【构建过程结束 】
BUILD SUCCESSFUL in 14s
24 actionable tasks: 19 executed, 5 up-to-date
PS:Task 配置运行在「配置阶段」,Task 动作运行在「执行阶段」。
监听 Task 有向无环图生成,在setting.gradle文件
gradle.getTaskGraph().addTaskExecutionGraphListener(new TaskExecutionGraphListener() {
@Override
void graphPopulated(TaskExecutionGraph graph) {
println '【Task 有向无环图生成】'
}
})
执行阶段
在获得 Task 的有向无环图之后,执行阶段就是根据依赖关系依次执行 Task 动作。
监听构建过程的耗时情况
long beginOfSetting = System.currentTimeMillis()
def beginOfConfig = false
def configHasBegin = false
def beginOfProjectConfig = new HashMap()
def beginOfProjectExecute
gradle.projectsLoaded {
println "【初始化阶段结束】,总共耗时:" + (System.currentTimeMillis() - beginOfSetting) + "ms"
}
gradle.beforeProject { project ->
if (!configHasBegin) {
configHasBegin = true
beginOfConfig = System.currentTimeMillis()
}
beginOfProjectConfig.put(project, System.currentTimeMillis())
}
gradle.afterProject { project ->
def begin = beginOfProjectConfig.get(project)
println "【配置 " + project + "】,耗时:" + (System.currentTimeMillis() - begin) + "ms"
}
gradle.taskGraph.whenReady {
println "【配置阶段结束】,总耗时:" + (System.currentTimeMillis() - beginOfConfig) + "ms"
beginOfProjectExecute = System.currentTimeMillis()
}
gradle.taskGraph.beforeTask { task ->
task.doFirst {
task.ext.beginOfTask = System.currentTimeMillis()
}
task.doLast {
println "【执行 " + task + "】,耗时:" + (System.currentTimeMillis() - task.beginOfTask) + "ms"
}
}
gradle.buildFinished {
// 如果工程中包含 buildSrc 目录,对 buildSrc 项目的构建在这里会 beginOfProjectExecute 空指针
if(null != beginOfProjectExecute){
println "【执行阶段结束】,总耗时:" + (System.currentTimeMillis() - beginOfProjectExecute) + "ms"
}
}
Task的生命周期
每个 task 都会经历 初始化、配置、执行 这一套完整的生命周期流程。
按照构建流程,下面是我对task生命周期的一个理解
- 在gradle构建中的配置阶段,会开始对task进行配置,这时候不会执行task
- 在gradle构建中的执行阶段,就会开始执行各个task
简单描述下 Android Gradle Plugin 中的 Variant 概念
variants,中文译为变体,在理解这个单词之前,就必须先了解 buildType、flavor、dimension 与 variant 之间的关系
buildTypes 位于模块级build.gradle文件的android{}代码块内,它是用来创建和配置构建类型的。
flavor的作用
- 在一个分支上,编译不同的版本(包名/应用图标/debug/release)
- 多个分支代码片段/so库,合并到同一分支进行维护,并且可以根据不同的Build Variant 进行选择性编译;
在 android gradle plugin V3.x 之后,每个 flavor 必须对应一个 dimension,可以理解为 flavor 的分组,然后不同 dimension 里的 flavor 会组合成一个 variant。
flavorDimensions "size", "color"
productFlavors {
big {
dimension "size"
}
small {
dimension "size"
}
blue {
dimension "color"
}
red {
dimension "color"
}
}
根据上述配置 Gradle 会创建以下构建变体:
- bigBlueDebug
- bigRedDebug
- smallBlueDebug
- smallRedDebug
- bigBlueRelease
- bigRedRelease
- smallBlueRelease
- smallRedRelease
因此在我的理解下,认为variant的概念就是根据不同配置生成的变体,每个 variant 可以对应的使用 variantImplementation 来引入特定的依赖,比如:bigBlueImplementation,只有在 编译 bigBlue variant的时候才会引入,能够更方便地处理多个不同版本的问题。
在gradle中,有一个关于variaint的基本数据的类,如下:
public abstract class BaseVariantData implements TaskContainer {
private final GradleVariantConfiguration variantConfiguration;
private VariantDependencies variantDependency;
private final VariantScope scope;
public Task preBuildTask;
public Task sourceGenTask;
public Task resourceGenTask; // 资源处理
public Task assetGenTask;
public CheckManifest checkManifestTask; // 检测manifest
public AndroidTask<PackageSplitRes> packageSplitResourcesTask; // 打包资源
public AndroidTask<PackageSplitAbi> packageSplitAbiTask;
public RenderscriptCompile renderscriptCompileTask;
public MergeResources mergeResourcesTask; // 合并资源
public ManifestProcessorTask processManifest; // 处理 manifest
public MergeSourceSetFolders mergeAssetsTask; // 合并 assets
public GenerateBuildConfig generateBuildConfigTask; // 生成 BuildConfig
public GenerateResValues generateResValuesTask;
public Sync processJavaResourcesTask;
public NdkCompile ndkCompileTask; // ndk 编译
public JavaCompile javacTask;
public Task compileTask;
public Task javaCompilerTask; // java 文件编译
// ...
}
工具汇总
gradle dsl 查询地址 https://docs.gradle.org/current/dsl/index.html
android gradle plugin dsl 查询地址 https://google.github.io/android-gradle-dsl/
参考文章
https://www.jianshu.com/p/6dc2074480b8
https://juejin.cn/post/6917486983946338318/#heading-13