在Android的gradle plugin代码中,常常看到这样的代码:

  1. public void buildTypes(Action<? super NamedDomainObjectContainer<BuildType>> action) {
  2. checkWritability();
  3. action.execute(buildTypes);
  4. }

这段代码对应着这样一段dsl:
buildTypes {
release {
ndk {

}
}
}
在groovy的语法中,这相当于用闭包作为参数调用了buildTypes方法,该方法内随后又用buildTypes这个NamedDomainObjectContainer实例调用了闭包方法。
至此为止都没有什么问题,但是奇怪的在于:

  • 在闭包中,delegate被设置成了NamedDomainObjectContainer实例;
    • 这是由Gradle通过AST来实现的。
      • 这个细节即不是标准的groovy行为,也没有文档说明,感觉要理解gradle是比较困难的,社区更多的将gradle的dsl语法当作黑盒来用,没有人追究细节,没有人提供更清晰的文档。
      • NamedDomainObjectContainer对象实例需要由Project#container方法来创建。
  • release {…} 看起来是一个方法调用,但是NamedDomainObjectContainer并没有实现一个名为release的方法
    • 实际上发生的事情是:NamedDomainObjectContainer包含的类:BuildType必须要有一个name属性,或者有一对getName/setName方法,这样语句的意思是:使用字符串”release”设置BuildType的name属性,然后将BuildType作为delegate,调用release后面的闭包。
    • release闭包内的ndk其实是在调用BuildType#ndk方法。

总之,在Gradle的世界中,常常有三种以上的方式来做到同一件事情,并且它们的语法还都各不相同,如果你不了解这些写法的官方黑话,就很难理解这种语法,只能通过猜测来获知一段代码的意图,不过,好在大多数时候,由于dsl的合理的命名,猜测都是准确的。如果希望研究gradle的各种API,不仅仅要参考JavaDoc,还需要参考dsl doc,因为有很多写法都是通过AST转换的,并不是标准的groovy语法,不对应着任何一个方法调用或者delegate属性。

自定义Configuration

Gradle的构建脚本中,dependencies块下的每一项都是一个Configuration配置,Java Plugin预定义好了一系列Configuration,如api, implementation等。
如果想要定义自己的Configuration,可以参考这里:https://docs.gradle.org/current/userguide/managing_dependency_configurations.html
如果看完了上面那篇文档,对于具体的语法还有疑问,想去翻翻Configuration或者Dependency相关文档查看具体方法定义,放弃吧。Gradle的DSL文档和JavaDoc文档都没有说明configuration块中自定义Configuration究竟是什么语法,在一个Configuration定义时发生了什么,估计又是哪个AST做了什么事情,如果真的想知道,就自己去Gradle源码里翻吧!Good Luck!Have fun with Gradle!(好消息是,通常只要按照Gradle文档中的示例来就行了,遇到问题再去社区里面问)

生命周期

  1. init阶段:在settings文件中调用include引入各个项目
    1. 引入项目时,只需要引入叶子节点,路径上的其他项目也都会被引入进来,背后的逻辑是如果要引入子项目,那么父项目当然也是需要引入的;
    2. 引入项目时,写法是:::…,是相对于根项目路径的子项目路径,如果要引入的项目路径是相对于当前路径的,可以直接省略,以冒号开头;
    3. 也可以使用includeFlat,这种方式引入的项目被视为相对于根目录父目录的项目;
  2. config阶段:配置前一步确定的各个Project对象,确定tasks
  3. exec阶段:运行各个Project中需要执行的tasks