简单了解下 groovy 语法,讲一下 groovy 里面的 owner 和 delegate 机制

groovy语法部分和java基本是类似的,特别也是闭包部分,这部分主要也和groovy的闭包相关

闭包

**Groovy中的闭包是一个开放,匿名的代码块,可以接受参数,返回值并分配给变量,并且是一个对象,

闭包的写法

  1. //执行一句话
  2. { printf 'Hello World' }
  3. //闭包有默认参数it,且不用申明
  4. { println it }
  5. //闭包有默认参数it,申明了也无所谓
  6. { it -> println it }
  7. // name是自定义的参数名
  8. { name -> println name }
  9. //多个参数的闭包
  10. { String x, int y ->
  11. println "hey ${x} the value is ${y}"
  12. }

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生命周期的一个理解

  1. 在gradle构建中的配置阶段,会开始对task进行配置,这时候不会执行task
  2. 在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 文件编译
    // ...
}

保存了很多的task

工具汇总

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