ReactNative进阶(十四):初探 Gradle
一、前言
Java
世界中主要有三大构建工具:Ant
、Maven
和Gradle
。经过几年的发展,Ant
几乎销声匿迹、Maven
也日薄西山,而Gradle
的发展则如日中天。
Maven
功能主要分为五点:依赖管理系统、多模块构建、一致的项目结构、一致的构建模型和插件机制。我们可以从这五个方面来分析Gradle
优于Maven
的先进之处。
二、依赖管理系统
Maven
为Java
世界引入了一个新的依赖管理系统。在Java
世界,可以用groupId
、artifactId
、version
组成的Coordination
(坐标)唯一标识一个依赖。任何基于Maven
构建的项目自身也必须定义这三项属性,生成的包可以是Jar
包,也可以是war
包或者ear
包。一个典型的依赖引用如下所示:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
从上面可以看出当引用一个依赖时,version
可以省略掉,这样在获取依赖时会选择最新的版本。而存储这些组件的仓库有远程仓库和本地仓库之分。远程仓库可以使用世界公用的central
仓库,也可以使用Apache Nexus
自建私有仓库;本地仓库则在本地计算机上。通过Maven
安装目录下的settings.xml
文件可以配置本地仓库的路径,以及采用的远程仓库地址。
Gradle
在设计的时候基本沿用了Maven
的这套依赖管理体系。不过它在引用依赖时还是进行了一些改进。首先引用依赖方面变得非常简洁。
dependencies {
compile 'org.hibernate:hibernate-core:3.6.7.Final'
testCompile ‘junit:junit:4.+'
}
第二,Maven
和Gradle
对依赖项的scope
有所不同。在Maven
世界中,一个依赖项有6种scope
,分别是complie
(默认)、provided
、runtime
、test
、system
、import
。而gradle
将其简化为了4种:compile、runtime、testCompile、testRuntime
。
那么,如果想在gradle
使用类似于provided
的scope
怎么办?别着急,由于gradle
语言的强大表现力,我们可以轻松编写代码来实现类似于provided scope
的概念。
第三,Gradle
支持动态的版本依赖。在版本号后面使用+
号的方式可以实现动态的版本管理。
第四,在解决依赖冲突方面Gradle
的实现机制更加明确。使用Maven
和Gradle
进行依赖管理时都采用的是传递性依赖;而如果多个依赖项指向同一个依赖项的不同版本时就会引起依赖冲突。Maven
处理这种依赖关系往往是噩梦一般的存在。而Gradle
在解决依赖冲突方面相对来说比较明确。
三、多模块构建
在SOA
和微服务的浪潮下,将一个项目分解为多个模块已经是很通用的一种方式。在Maven
中需要定义parent POM
作为一组module
的聚合POM
。在该POM
中可以使用<modules>
标签来定义一组子模块。parent POM
不会有什么实际构建产出。而parent POM
中的build
配置以及依赖配置都会自动继承给子module
。
Gradle
也支持多模块构建。而在parent
的build.gradle
中可以使用allprojects
和subprojects
代码块来分别定义里面的配置是应用于所有项目还是子项目。对于子模块的定义是放置在setttings.gradle
文件中的。在gradle
的设计当中,每个模块都是Project
的对象实例。而在parent build.gradle
中通过allprojects
或subprojects
可以对这些对象进行各种操作。这无疑比Maven
要灵活的多。
比如在parent
的build.gradle
中有以下代码:
allprojects {
task hello << { task -> println "I'm $task.project.name" }
}
执行命令gradle -q hello
会依次打印出父module
以及各个submodule
的项目名称。这种强大的能力能让gradle
对各个模块具有更强的定制化。
四、一致的项目结构
在Ant
时代,大家创建Java
项目目录时比较随意,然后通过Ant
配置指定哪些属于source
,那些属于testSource
等。而Maven
在设计之初的理念就是Conversion over configuration
(约定大于配置)。其制定了一套项目目录结构作为标准的Java
项目结构。
Gradle
也沿用了这一标准的目录结构。如果在Gradle
项目中使用了标准的Maven
项目结构,那么在Gradle
中也无需进行多余的配置,只需在文件中包含apply plugin:'java'
,系统会自动识别source
、resource
、test srouce
等相应资源。不过Gradle
作为JVM
上的构建工具,也同时支持groovy
、scala
等源代码的构建,甚至支持Java
、groovy
、scala
语言的混合构建。虽然Maven
通过一些插件(比如maven-scala-plugin
)也能达到相同目的,但配置方面显然Gradle
要更优雅一些。
五、一致的构建模型
为了解决Ant
中对项目构建活动缺乏标准化的问题,Maven
特意设置了标准的项目构建周期,其默认的构建周期如下所示:
<phases>
<phase>validate</phase>
<phase>initialize</phase>
<phase>generate-sources</phase>
<phase>process-sources</phase>
<phase>generate-resources</phase>
<phase>process-resources</phase>
<phase>compile</phase>
<phase>process-classes</phase>
<phase>generate-test-sources</phase>
<phase>process-test-sources</phase>
<phase>generate-test-resources</phase>
<phase>process-test-resources</phase>
<phase>test-compile</phase>
<phase>process-test-classes</phase>
<phase>test</phase>
<phase>prepare-package</phase>
<phase>package</phase>
<phase>pre-integration-test</phase>
<phase>integration-test</phase>
<phase>post-integration-test</phase>
<phase>verify</phase>
<phase>install</phase>
<phase>deploy</phase>
</phases>
而这种构建周期也是Maven
最为人诟病的地方。因为Maven
将项目的构建周期限制的太死,你无法在构建周期中添加新的phase
,只能将插件绑定到已有的phase
上。而现在项目的构建过程变得越来越复杂,而且多样化,显然Maven
对这种复杂度缺少足够的应变能力。
比如你想在项目构建过程中进行一项压缩所有javascript
的任务,那么就要绑定到Maven
现有的某个phase
上,而显然貌似放在哪个phase
都不太合适。而且这些phase
都是串行的,整个执行下来是一条线,这也限制了Maven
的构建效率。而Gradle
在构建模型上则非常灵活。在Gradle
世界里可以轻松创建一个task,并随时通过depends
语法建立与已有task的依赖关系。甚至对于Java
项目的构建来说,Gradle
是通过名为java
的插件来包含了一个对Java
项目的构建周期,这等于Gradle
本身直接与项目构建周期是解耦的。
六、插件机制
Maven
和Gradle
设计时都采用了插件机制。但显然Gradle
更胜一筹。主要原因在于Maven
是基于XML
进行配置。所以其配置语法太受限于XML
。即使实现很小的功能都需要设计一个插件,建立其与XML
配置的关联。比如想在Maven
中执行一条shell
命令,其配置如下:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2</version>
<executions>
<execution>
<id>drop DB => db_name</id>
<phase>pre-integration-test</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>curl</executable>
<arguments>
<argument>-s</argument>
<argument>-S</argument>
<argument>-X</argument>
<argument>DELETE</argument>
<argument>http://${db.server}:${db.port}/db_name</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
而在Gradle
中则一切变得非常简单。
task dropDB(type: Exec) {
commandLine ‘curl’,’-s’,’s’,’-x’,’DELETE’,"http://${db.server}:{db.port}/db_name"
}
在创建自定义插件方面,Maven
和Gradle
的机制都差不多,都是继承自插件基类,然后实现要求的方法。
从以上五个方面可以看出Maven
和Gradle
的主要差异。Maven
的设计核心Convention Over Configuration
被Gradle
更加发扬光大,而Gradle
的配置即代码又超越了Maven
。
在Gradle
中任何配置都可以作为代码被执行的,也可以随时使用已有的Ant
脚本(Ant task
是Gradle
中的一等公民)、Java类库
、Groovy类库
来辅助完成构建任务的编写。
这种采用本身语言实现的DSL
对本身语言项目进行构建管理的例子比比皆是。比如Rake
和Ruby
、Grunt
和JavaScript
、Sbt
和Ruby
…..而Gradle
之所以使用Groovy
语言实现,是因为Groovy
比Java
语言更具表现力,其语法特性更丰富,又兼具函数式的特点。这几年兴起的语言(比如Scala、Go、Swift
)都属于强类型的语言,兼具面向对象和函数式的特点。
另外,Gradle
的命令行也比Maven
的要强大的多。
七、延伸阅读
常用命令如下:(linux下是./gradlew
,该脚本在项目下,windows直接gradlew即可)
./gradlew -v
版本号,首次运行,没有gradle
的要下载的哦。
./gradlew clean
删除build
文件夹;./gradlew build
检查依赖并编译打包;
这里注意的是 ./gradlew build
命令把debug
、release
环境的包都打出来,生成的包在类似目录HelloWord/app/build/outputs/apk/
下。如果正式发布只需要打release
的包,该怎么办呢,下面介绍一个很有用的命令 assemble
, 如
./gradlew assembleDebug
编译并打Debug包;./gradlew assemblexiaomiDebug
编译并打xiaomi的debug包,其他类似;./gradlew assembleRelease
编译并打Release的包;./gradlew assemblexiaomiRelease
编译并打xiaomi的Release包,其他类似;./gradlew installRelease
Release模式打包并安装;./gradlew uninstallRelease
卸载Release模式包;
八、拓展阅读
- 《RN合集》