Grdle构建原理分析

Gradle是安卓常用的构建工具。本文通过对Gradle构建文件的拆解,以及编写Gradle插件实践,来说明Gradle构建的本质原理。帮助大家更好的理解和使用Gradle构建工具。

  • 一、Grovvy

Gradle是构建工具,而Grovvy是Gradle所使用的语言。该语言主要设计的思想是,隐藏语言实现的细节,让开发这可以像写配置文件一样来使用它

1Closure

Glosure是Grovvy的语法,翻译过来叫“闭包”。它实际上是将方法当作参数来传递。

我们知道在java里methodA(methodB()), methodA使用的参数是methodB的返回值,而无法做到将方法本身做为参数传递。如下图传递方法本身的形式,java语言是无法做的。java语言提供匿名内部类和lamda表达式的形式来传递方法,它实际上传递的是类的对象,而不是方法本身。

Grovvy提供Closure则可以用于传递方法,右侧是执行结果:

 

了解了Gvovvy的闭包语法,我们来拆解看一下下面的配置:

Grovvy语言对最后的参数不加括号也行,实际上完整的写法是这样:

这里每一个都是方法调用,Ctrl + 鼠标左键可以跳转查看:

classpath是一个特殊方法, 跳转之后是看不到的。这里实际上是用到Grovvy的methodMissing语法特性。

Grovvy是一种动态语言,无法在运行之前确定一个对象属于哪个接口哪个类,甚至无法在运行之前确定方法或属性是否存在。它提供了methodMissing语法,当运行时找不到某个方法,则调用他的methodMissing方法看是否能够处理。

methodMissing签名:”void methodMissing(String name, Object args){}

第一个参数是调用过来方法的名字,第二个参数是方法携带的参数。

示例

执行结果:

因此,上面说到的classpath方法,实际上调用的是DependencyHandler的子类DefaultDependencyHandler.grovvy中的methodMissing方法:

repositories定义的是查找url的路径,dependencies是这个工程可能会用到的插件,classpath是定义插件的路径。简单总结:repositories控制classpath的来源,classpath控制插件的来源。

2、Action

Grvovvy方法的参数除了Closure还有Action。下左图中是我们常见的方法allprojects,它实际上传递的参数是一个Action,完整写法如下右图。

 

Action是一个接口,提供execute方法,与java的匿名内部类是一样的。

  • 二、常用方法

了解了Grovvy主要语法,继续对Gradle主要配置方法进行拆解,进一步的理解Gradle构建。

1、buildType

该方法提供的是一种差异化编译打包方案。默认有release/debug两个方法。在BuildVariants进行切换选择。它表示编译main目录+release目录或者main目录+debug目录的内容。这样release和debug两个目录,就可以进行差异化的实现。

该方法也可以自定义打包目录,如下图,定义了mydemo的buildType,sync project后,在Build Variant可以选择变体。在工程目录下建立mydemo目录,即可进行差异化开发和编译。这里自定义变体的本质,既是使用的上面说到的methodMissing语言特性。

2、flavorDimensions

flavor也是一种差异化编译方案,是和buildType的“排列组合”。下图定义了“isExport”维度,在变体中结合debug/release一共生成四个变体。如exportRelease变体,表示的是编译main+export+release三个目录。

如果flavorDimensions指定超过两个维度,则在productFlavors中必须要指明所使用的维度。

3、implementationcompileapi

这三个都是引入依赖的方法,他们的区别简单来说:implementation提供非传递依赖,compile和api提供传递依赖。

 传递依赖 vs 非传递依赖

传递依赖,projectA依赖library1 ( compile project (‘:library1’)), library1依赖library2。则projectA中可以看到和直接使用library2中的内容。非传递依赖,则在projectA不能看到library2的内容。

非传递依赖的好处,是在projectA编译的时候,不会一起编译library2的内容。也就是library2的改变,不会导致projectA重新编译。

我们知道构建工程分为编译打包两个过程。编译主要是将源码编译为字节码,打包是将编译后字节码文件进行优化和打包。

通过非传递依赖,上例library2修改后,只需要在打包过程将修改后library2的编译文件打包进来即可。与传递依赖相比,节约了projectA重新编译的时间。

说完非传递依赖,传递依赖也就清楚了。projectA如果传递依赖library2。library2发生改变,将导致projectA被重新编译。

传递依赖的使用场景,通常是依赖网络上的资源文件。因为这些资源更新频率往往比较低,很长时间更新一次,进行一次重新编译的开销是可以接受的。同时传递依赖到当前工程,方便对源码进行查看。

在本地的项目通常建议使用非传递依赖,因为本地相对更新较为频繁,减少主项目因为依赖项目更新而产生编译的开销。

事实上,老的版本Gradle只提供compile方法,新的版本提供了非传递依赖的implementation方法和传递依赖的api方法。api理解为新版本的compile改了个名字而已。

4、gradleWrapper

wrapper翻译过来是“糖纸”的意思。它的作用是包住gradle,实际上做工作的是它里面的gradle。执行gradlew,如果本地有gradle并且符合配置则直接使用,否则从指定的地址去下载。gradle-wrapper.properties用于定义gradle的路径和下载地址。通过gradle wrapper,在项目配置时只需要指定gradle的版本,不用将gradle放到项目里。从而减小项目的体积。

5、task

task是gradle构建正真执行的部分。通过执行一系列的task完成gradle构建的整个过程。前面说的buildType flavorDimensions生成的变体,实际上是通过插件根据配置创建出不同的task(后面准备分享一票Android apk编译打包的过程,这里再详细进行介绍)。

看一个简单的示例:

定义一个clean任务,执行gradlew clean会将指定目录进行删除。“rootProject.buildDir”是工程的build目录,在我这里是“E:\Code\MyGradleDemo\build”。

 

这里gradlew clean命令需要注意一下,它包含两个过程,配置和执行。配置(单独执行gradlew就是只执行配置),会扫描task里的每一行代码。并将“任务执行体”(“delete rootProject.buildDir”)进行记录。等到执行阶段,再执行正真的操作。实际的执行顺序是先依次打印“start”、“end”然后再删除build目录。

如果单独执行gradlew,则可以看到“start”和“end”的打印,但是build目录不会被删除,因为只执行了配置。

task中有doFrist和doLast方法,表示在任务真正执行之前和执行之后做的工作。

 

dependsOn关键字表示当前任务需要依赖的任务。执行该任务之前会先执行依赖的任务。用该关键字建立任务之间的依赖网,实际是一张有向无环图。从而完成复杂的构建过程。

 

  • 三、Gradle构建过程

前面章节对Gradle构建文件主要方法进行拆解,大概知道了Gradle构建有哪些内容,以及他们是如何实现的。下面把这些片段联系起来,整体看一下项目构建的过程。

项目构建主要分为三个阶段:初始化阶段、定义阶段和执行阶段。

初始化阶段,即执行settings.gradle文件,依次执行inlude方法,组织构架整个项目的结构。

定义阶段,又叫“配置阶段”,依次执行每个模块的build.gradle,生成一系列的task,并根据task之间依赖关系生成tasks的有向无环图。

执行阶段,按照tasks依赖图逐个执行task,完成构建。

  • 四、Gradle插件

Gradle的功能主要是通过插件来实现。在build.gradle通过插件在配置阶段生成Task,并在执行阶段执行,完成项目构建。

1、插件介绍

下图是application插件,它是用于构建Android apk的插件,下面”andrdoid{…}”(语言就是前面说的Closure)称为插件的扩展,通过扩展给插件提供配置参数。

下面自己写一个简单的插件和扩展,看一下插件执行的原理。

自定义插件需要实现Plugin<T>接口,通过Project.exteinstions.create来创建一个扩展。示例的扩展名是“demoPlugin”,后续就使用该扩展名来配置扩展参数。

Project表示当前构建,即理解为当前build.gralde文件。

“apply plugin”是执行插件的命令,它会调用插件的apply方法。

“afterEvaluate”表示构建执行完成后执行。因为插件都是先执行,再提供扩展。因此,要获取用自定义扩展的参数,需要放到afterEvaluate里面。

执行构建,查看打印输出。

插件的执行顺序:先执行apply方法,创建扩展;然后执行扩展配置参数;最后执行afterEvalute,使用参数执行插件功能。

2、编写插件项目

上面是在build.gradle直接编写和使用插件。我们了解了插件执行的基本原理。实际上插件的用法是定义和使用相分离。即进行简单的配置,直接使用apply plugin 或plugins{id xx},使用已存在的插件。

下面讲一下AS提供的buildSrc实现插件项目的方法。buildSrc是一个保留的工程目录,他会在settings.gradle执行之后,其他各个模块build.gralde执行之前被自动进行构建。

buildSrc命名,以及其中一些子目录的命名是固定的。

1、首先创建名称为buildSrc的模块。创建src/main/java目录。在main目录下创建resroures/META-INF/gradle-plugins目录。这些目录的名称是固定的,不能写错。创建成功AS会给出特使标志,见下图。

2、在buildSrc项目的build.gradle中增加对gradle依赖。

3、在java目录下编写插件。

这里直接使用java语言来编写插件。Grovvy语言相对来说比较小众,因为一些语法不熟悉导致时间和精力的浪费得不偿失。android apk构建插件就是用java语言写的,还有一些知名插件使用kotlin编写。当前用java语言或kotlin语言编写Gradle插件是一个趋势。

4、插件编写完成后,在上面新建的gradle-plugins目录中加入xxx.properties配置文件。”xxx”可以随意命名,它就是未来在其他项目使用插件的id。在该文件中用implementation-class配置刚才编写好的插件。

5、在其他项目中使用该插件

同步配置执行结果:

自定义插件应用成功。

  • 21
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值