赞
踩
1.Android项目中的一些build.gradle分析
- apply plugin: 'com.android.application'
- apply from: 'test.gradle'
-
- allprojects {
- repositories {
- google()
- jcenter()
- }
- }
最初的疑问:
想这几句gradle的设置是什么含义?其中apply是什么,做了什么?
allprojects、repositories这些语句设置了什么,为什么这样写?
apply实际上时一个方法,它接收一个闭包作为参数,参数plugin:"java"实际上是一个map的结构,键值对。
repository这段代码实际上也是一个方法的调用,参数是一段闭包。这是gradle编译脚本中的特殊语法。
我们可以查看一下build.gradle脚本的代码:
从源码可以看到:
- void allprojects(groovy.lang.Closure closure);
- void afterEvaluate(groovy.lang.Closure closure);
- void dependencies(groovy.lang.Closure closure);
- void buildscript(groovy.lang.Closure closure);
在Project这个接口类中定义了很多的方法,比如allprojects,他们几乎都拥有一个Closure类型的参数。
刚开始接触到gradle时候,一直以为这个gradel.build文件就是这样的固定写法。后来了解了一些编译的知识才明白,gradle文件也是一种特定语言编写的的文件。而这种语言不常见,而Closure就是它的特殊定义之一。
Closure是什么类型?这里先要了解一些gradle默认编写的语言。
Gradle默认是由groovy语言来编写的,了解groovy的人大概知道,groovy是一种面相对象的语言,因此和java、kotlin有很多相似的地方。
groovy官网:The Apache Groovy programming language
Groovy中一些特别的点
1.闭包
简单的理解,闭包就是一段由大括号括起来的代码块,它有一个类型声明:groovy.lang.Closure closure。通常我们可以定义一段代码块作为参数传递给一个参数是Closure类型的方法调用。
- /**
- * 定义一个clause
- */
- def ageClosure = {
- int age = 20
- while (age<100){
- age++
- }
- }
- /**
- * 带参数的Cosure
- */
- def ageClosure = {
- param->
- int age = 20
- while (age<100){
- age++
- }
- println "$param--$age"
- }
- //不同的调用closure方式
- def printAge(Closure closure){
- println "==================================="
- closure.call("这是一个参数")
- println "==================================="
- }
- //直接作为一个方法调用
- ageClosure("text")
- ageClosure()
- //作为参数
- printAge(ageClosure)
闭包看起来和kotlin的一些语法比较像,有什么区别:
- //kotlin
- @Test
- fun main(){
- var list = mutableListOf<String>()
- list.apply {
- add("java")
- add("kotlin")
- add("html")
- }
- list.forEach {
- println(it)
- }
- }
- //groovy文件中的代码
- List<String> list = new ArrayList<>()
- list.add("java")
- list.add("kotlin")
- list.add("groovy")
- list.add("python")
-
- /**
- * 普通groovy List的forEach方法
- */
- list.forEach{
- println(it)
- }
可以看到对于一些语法kotlin和groovy的使用几乎一样。groovy的闭包使用和kotlin的语法也很像:
- /**
- * 闭包在方法中的使用,和kotlin语法类似
- */
- def targetFile = new File("file.txt")
- targetFile.eachLine {
- println(it)
- }
代码从一个file.txt文件按行读取文件,然后传递一个闭包,在闭包中打印出每行字符。这里的eachLine方法和前面的kotlin的语法相似,但是参数不同:kotlin的forEach方法
- public inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit {
- for (element in this) action(element)
- }
参数是一个返回Unit的代码块,而groovy的eachLine方法参数是一个Closure闭包。
2.省略
Groovy语言由于其特性,可以做一些简化的省略,比如省略方法的括号,省略参数的引号。
比如apply()方法。实际上是:
void apply(Map<String, ?> options);
我们常用的println()方法,可以省略括号。
- tasks.register("hi"){
- it.doLast{
- println("hellp groovy ~")
- println "hello groovy !!"
- }
- }
代码闭包的形式,再加上省略,以及固定的一些写法,可能是刚开始接触build.gradle难理解的几个原因。
关于gradle官方有他的介绍:What is Gradle?
总结来说就是几个字:Gradle是高性能的、可编程的、基于JVM的、可扩展的编译框架。
1.在Gradle之前的代码编译工具Ant和maven
Ant构建项目
Ant是早期Apache开发的用于构建java项目的构建工具,它是用xml来编写编译配置文件的,内部内置了很多预定义的任务,方便Java开发着编译,部署项目。
一个Hello world Java项目使用ant 编译项目的配置文件build.xml
- <?xml version = "1.0"?>
- <project name = "fax" basedir = "." default = "build">
- <property name = "src.dir" value = "src"/>
- <property name = "web.dir" value = "war"/>
- <property name = "build.dir" value = "${web.dir}/WEB-INF/classes"/>
- <property name = "name" value = "fax"/>
- <path id = "master-classpath">
- <fileset dir = "${web.dir}/WEB-INF/lib">
- <include name = "*.jar"/>
- </fileset>
- <pathelement path = "${build.dir}"/>
- </path>
- <target name = "build" description = "Compile source tree java files">
- <mkdir dir = "${build.dir}"/>
- <javac destdir = "${build.dir}" source = "1.5" target = "1.5">
- <src path = "${src.dir}"/>
- <classpath refid = "master-classpath"/>
- </javac>
- </target>
- <target name = "clean" description = "Clean output directories">
- <delete>
- <fileset dir = "${build.dir}">
- <include name = "**/*.class"/>
- </fileset>
- </delete>
- </target>
- </project>
其中:
src.dir指的是可以找到java源文件的项目的源文件夹
build.dir是指项目编译的输出文件夹
web.dir是指项目的Web源文件夹,您可以在其中找到JSP,web.xml,css,javascript和其他Web相关文件
有一个name是clean的target和build的target,从名字可以看出这是用来clean和build操作的。
Ant参考 Ant - 介绍_学习Apache Ant|WIKI教程
Maven 构建项目
maven是Apache中的一个编译和管理工具,主要对Java项目进行构建和管理。maven能够帮助开发人员完成项目的构建、文档生成、依赖、发布、分发等操作。
使用命令创建一个基于maven编译的项目的pom.xml
- <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>com.companyname.bank</groupId>
- <artifactId>consumerBanking</artifactId>
- <packaging>jar</packaging>
- <version>1.0-SNAPSHOT</version>
- <name>consumerBanking</name>
- <url>http://maven.apache.org</url>
- <dependencies>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>3.8.1</version>
- <scope>test</scope>
- </dependency>
- </dependencies>
- </project>
从pom. xml代码中,可知 Maven 已经添加了 JUnit 作为测试框架。
执行mvn clean package 可以完成 clean 并打包生成一个jar
网上一个典型的maven构建的生命周期图:
maven中引入第三方依赖:
通过给pom.xml中添加一个dependencies节点
- <dependencies>
- <!-- 在这里添加你的依赖 -->
- <dependencies>
- <dependency>
- <groupId>com.companyname.common-lib</groupId><!-- 库名称,也可以自定义 -->
- <artifactId>common-lib</artifactId><!-- 库名称,也可以自定义 -->
- <version>1.0.0</version><!--版本号-->
- </dependency>
- <dependencies>
- </dependencies>
maven 参考 Maven 教程 | 菜鸟教程
Ant、maven、Gradle的区别和联系
Ant是最早的构建工具之一,它可以定义不同的task来处理任务,然后执行命令完成编译。但是用户发现ant有一个很大的缺陷,不能管理依赖,对于一些第三方的库,在使用的时候都需要手动拷贝到lib目录,这个操作很烦且容易出错。
后来为了解决依赖管理的问题,Apache提出了maven,maven最大的改进在于提出了仓库的概念。开发者可以把所有的依赖都放到仓库中,在项目的管理文件中,我们只需要标注需要什么包,什么版本,maven就自动去对一个的仓库寻找,并且打包到项目中来。对应了pom.xml文件中dependency节点。maven改进了ant的target,增加了编译的生命周期等一些新概念。
maven已经可以满足大部分工程的构建,但是由于maven也是xml文件配置的,语法不够简单,不能实现随意的编程设置,并且maven不支持自定义任务,只能以插件的方式工作。Gradle在maven基础上出现了,它充分利用了maven的资源,继承了maven的中央仓库。比起xml的写法,gradle使用面相对象的语言来编程脚本,可以像编写其他程序一样来编写编译工具。后来有一些开发工具开始集成gradle作为编译工具,更进一步简化了gradle脚本的使用,创建项目会帮开发者自动创建基本的脚本代码。而我们如果想添加一个第三方依赖,只需要在build.gradle的dependencies方法中添加一条引用的字符串,IDE就能自定帮我们从指定的仓库中下载。这就是我们现在使用的工具了。
2.Gradle的编译流程
1.初始化阶段
gradle支持单项目和多项目的构建,在初始化阶段,gradle决定哪些项目将会参与构建,并为每个项目创建Project对象实例。同时在初始化阶段会更加settting.gradle文件创建一个Setting对象,来配置多项目的Project。
2.配置阶段
在配置阶段,gradle会分别在每个Project上执行build.gradle,并配置Project对象。
3.执行阶段
在执行阶段,gradle会判断配置阶段创建的哪些Task将要执行,然后执行选中的Task。
响应构建生命周期
在gradle的执行生命周期过程,可以给build.gradle添加一些方法从而添加我们自己的处理流程,如:afterEvaluate()方法来给一个添加一个closure,这个方法添加一个listener监听一个项目的build的过程,在build之后就会执行。
- afterEvaluate {
- def assemble = project.tasks.findByName("assemble")
- println("task-name:"+assemble+"---1enable:${assemble.enabled}")
- assemble.setEnabled(false)
- println("task-name:"+assemble+"---2enable:${assemble.enabled}")
- println("size:"+set.size())
- println("ext--name==${ext.name}")
- }
Build Lifecycle 构建流程,生命周期
3.Gradle中一些基本的类
Gradle类
Gradle是基础类,通过project对象的getGradle可以获取到对象,它提供了几个访问项目的方法,以及一些添加回调的方法。比如:allprojects(action)、afterProject。
Project类
Project是使用Gradle最主要的类之一,我们每个build.gradle文件在编译的过程都会被组装成一个project对象实例。提供了基础的访问项目的方法,我们常用的
buildscript、dependencies都是它的方法。
Task类
Task代表了gradle系统中的一个原子操作,比如编译class文件或者生成一个doc文件。没一个Task都属于一个project,我们可以使用一些简单的方法创建task对象。
- task myTask
- task myTask { configure closure }
- task myTask(type: SomeType)
- task myTask(type: SomeType) { configure closure }
其中task关键字,实际上时一个方法
Gradle系统提供的一些基础的Task操作:assemble、delete、clean、build
https://docs.gradle.org/current/userguide/base_plugin.html#sec:base_tasks
Task之间是可以相互依赖的,某个task的执行可能要依赖于某个task的结果。
截取官网gradle介绍的两个不同的gradle任务,他们的Task依赖关系图:
Task Action
每个Task由一些列的有序的Action组成,当一个task执行的时候会调用action的execute执行。我们在build.gradle文件中一般可以通过
Task.doFirst(org.gradle.api.Action) or Task.doLast(org.gradle.api.Action)添加一个Action来执行我们的代码逻辑。
Setting类
Setting官方定义:声明实例化和配置参与构建的项目实例的层次结构所需的配置。我们通常使用Setting的两个功能:
1.组织多moudle项目的项目结构,让gradle编译时区别是单项目构建还是多项目构建。
include ':flutter_xxx'
2.设置一些属性
除了此接口的属性之外,settting对象还为设置脚本提供了一些额外的只读属性。包括:buildCache、extensions、plugins、rootProject等。
3.设置Gradle
Gradle提供了一些通过属性和方法设置编译环境的方式。这些设置能够改变编译的参数,根据我们的需求优化编译效果,提高编译速度等。
我们在版本控制中保存一些设置比如 JVM 内存配置和 Java 主目录位置,以便整个团队在一致的环境中工作。我们只需要在gradle.properties文件中添加一些属性即可。
- gradle.properties中的一些属性设置,有一些是Android组件自定义的。
-
- # 这里是 robolectric 单元测试读取资源用的
- android.enableUnitTestBinaryResources=true
-
- base.dir=../Base/base
- structure.dir=../Structure/structure
- account.dir=../Account/account
- common.dir=../Common/common
- ad.dir=../Ad/ad
Gradle提供的其他一些设置:
org.gradle.caching=(true,false)
当设置为 true 时,Gradle 将在可能的情况下重用任何先前构建的任务输出,从而使构建速度更快
org.gradle.caching.debug=(true,false)
设置为 true 时,每个任务的单个输入属性哈希值和构建缓存键都会记录在控制台上
org.gradle.daemon=(true,false)
当设置为 true 时,Gradle 守护程序用于运行构建。默认为真
org.gradle.java.home=(path to JDK home)
为 Gradle 构建过程指定 Java 主目录。该值可以设置为 jdk 或 jre 位置,但是,根据您的构建功能,使用 JDK 更安全
org.gradle.jvmargs=(JVM arguments)
指定用于 Gradle 守护程序的 JVM 参数。该设置对于为构建性能配置 JVM 内存设置特别有用,这不会影响 Gradle 客户端 VM 的 JVM 设置
设置系统属性、环境变量、以及CI触发执行等,还可以设置通过http代理访问等。
官网设置
Build Environmenthttps://docs.gradle.org/current/userguide/build_environment.html
apply plugin:java 表示应用了gradle的java插件
apply plugin :MyClass 表示应用指定的class实现的插件。
1.gradle文件插件的实现
通过定义一个如config.gradle脚本文件的方式添加一个脚本插件。这个方式的插件简单方便,但是,这种插件在构建脚本之外不可见,我们不能在定义他的项目外重用。
2.buildSrc方式插件的实现
添加buildSrc moudle的方式,可以让一个项目的多个module使用。
可以将插件的源代码放在 rootProjectDir/buildSrc/src/main/java 目录(或 rootProjectDir/buildSrc/src/main/groovy 或 rootProjectDir/buildSrc/src/main/kotlin)具体取决于我们用哪种语言开发,同时我们在build.gradle中指定资源的位置。Gradle 自动编译和测试插件,这种插件在我们的项目子项目可以使用。
3.独立可发布的插件
创建一个插件,写几行简单的代码,然后使用发布命令发布到本地的maven仓库,通过classpath的方式引入插件,然后通过apply plugin:插件名称 方式使用插件。
通过classpath引入发布的可重用插件
通过将插件添加到构建脚本类路径然后应用插件,可以将已发布为外部 jar 文件的二进制插件添加到项目中。可以使用 buildscript {} 块将外部 jar 添加到构建脚本类路径中,如构建脚本的外部依赖项中所述。
官方示例:加载在指定的插件存储库gradlePluginPortal这个maven地址中的插件。
代码示例
- public class DiguaPlugin :Plugin<Project> {
-
- override fun apply(target: Project) {
- target.tasks.forEach {
- println("DiguaPlugin--task--${it.name}")
- }
- target.task("androidReaderTask"){
- it.doLast {
- println("android reader apply doLast Task")
- }
- }
- target.afterEvaluate {
- println("AndroidReader--自定义gradle插件 end")
- }
- }
-
- }
定义一个插件类,实现了Plugin接口,这个接口只有一个apply方法。
发布插件设置
- //引入发布的依赖
- dependencies {
- implementation gradleApi()
- implementation localGroovy()
- }
-
- //设置发布组件的Task
- uploadArchives {
- repositories {
- mavenDeployer {
- //本地的Maven地址设置为
- repository(url: uri('../repo'))
- pom.groupId = 'com.digua.android.plugin'
- pom.artifactId = 'diguaPlugin'
- pom.version = '1.0.6'
- }
- }
- }
在main目录下创建resource目录:
注意:这里的properties文件的名称就是最后我们apply或者plugins访问插件的名称
最后根据需求编写完成插件后执行对应的uploadArchives的Task就可以发布到对饮的仓库中。
使用组件
设置maven仓库,然后在classpath中添加gradle插件的路径。调用apply plugin:"pluginName"或者plugins中添加properties文件的名称这样就能访问到插件了。
1.没有Gradle如何编译项目?
2.Gradle还有哪些不常见的特性?
3.Gradle在项目编译中有什么可以改进的地方?
比如可以修改gradle.properties中的设置,根据需要调整编译配置:
比如修改org.gradle.jvmargs = -Xmx1536M,这个可以修改gradle编译VM的内存,影响到编译的速度。因为保活进程会保存每次编译的一些信息,内存大能够保存更多的编译信息,下次编译的时候不需要重新加载文件,直接提高了编译速度。同时也会影响增量编译等。
4.如何能通过修改优化gradle脚本,优化编译速度?
比如我们可以获取所有的Task(./gradlew tasks),然后通过编写条件,过滤掉一些编译不太重要的task,从而加快编译速度。
拓展
Gradle保活进程如何提高编译速度?
参考文献
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。