我和 Gradle 有个约会
发布于 8 个月前 作者 violinxliu 3390 次浏览 来自 技术

untitled1.png

本文来自于 腾讯 Bugly,是由 Bugly 邀请腾讯内部各位技术大咖,通过日常工作经验的总结以及感悟撰写而成,内容均属原创,非经作者同意,请勿转载。

讲个故事

Ant,我还真以为你是只蚂蚁

真正开始近距离接触编程其实是在2012年,年底的时候带我的大哥说,咱们这个 app 发布的时候手动构建耗时太久,研究一下 ant 脚本吧。 那个时候连 HashMap 都不知道是啥,可想开发经验几乎为零,一个小小的 ant 脚本看得我真是深深地感受到了这个世界充满的恶意。好在后来硬着头皮搞明白了什么 target 之类的鬼东西,不然就没有然后了。

Maven,你们真的会读这个单词么

Maven /`meivn/

接触 Maven,完全是因为读陈雄华的《Spring 实战》,他的源码居然是用 Maven 构建的,结果 Spring 学得一塌糊涂,Maven 我倒是用顺手了。。 跟 Ant 一样,Maven 可以用来构建 Java 工程;跟 Ant 一样,Maven 的配置用 xml 来描述;但,Maven 可以管理依赖,它可以让你做到“想要什么,就是一句话的事儿”。比如我想要个 gson,Maven 说可以,你记下来我带会儿构建的时候给你去取。

untitled2.png

真是让你当大爷呢。不过,Maven 这家伙学起来有点儿费劲,很多初学的时候在搭建环境的时候就被搞死了——你以为是因为 Maven 的学习曲线陡峭吗?当然不是,是因为当初 Maven 的中央仓库被 x 了,所以你就天天看着 cannot resovle dependencies 玩就好了。

后来 OSChina 傍上了阿里这个爸爸,就有了 maven.oschina.net。我去年找工作落定之后,想着做点儿什么的时候,发现 maven.oschina.net 估计被阿里爸爸关禁闭,死了几天,现在又活过来了。那又怎样呢,反正中央仓库被 x 的事情也已经成为过去。

#### Gradle,你爹是不是 Google!!

13年的时候,我兴奋地跟前面提到的大哥说 Maven 是个好同志的时候,大哥说,Google 推荐用 Gradle……所以,我想 Gradle,你爹是不是 Google……或者至少是个干爹吧。

其实这都不重要了,毕竟 Gradle 实在是好用。比起前面两位的 xml 配置的手段,直接用代码的方式上阵必然是灵活得多。不仅如此,Gradle 居然可以使用 Maven 仓库来管理依赖,就像是一个简易版的 Maven 一样,如果不是看不到 pom 文件,你都还以为你仍然在使用 Maven(当然,由于你在用 Maven 的仓库,所以你自然也是离不开 Maven 的)。哦,你是 Ant 用户啊,那也没关系啊,不信你看:

untitled3.png

用 Gradle 构建

1.1 工程结构

untitled4.png

如图所示,这是一个不能更普通的 android 的 gradle 工程了。

  • 根目录下面的 settings.gradle 当中主要是用来 include 子模块的,比如我们这个工程有一个叫做 app 的子模块,那么 settings.gradle 的内容如下:

  • untitled5.png

  • 根目录下面的 build.gradle 包含一些通用的配置,这些配置可以在各个子模块当中使用。

  • gradle.properties 文件包含的属性,会成为 project 的 properties 的成员,例如我们添加了属性 hello,

hello=Hello Tas! 然后我们在 build.gradle 当中创建 task:

untitled6.png

输出地结果是一样的: untitled7.png

  • local.properties 这个文件在 android 工程当中会遇到,我们通常在其中设置 android 的 sdk 和 ndk 路径。当然,这个 android studio 会帮我们设置好的。为了更清楚地了解这一点,我把 android 的 gradle 插件的部分源码摘录出来:

SDK.groovy,下面的代码主要包含了加载 sdk、ndk 路径的操作。

untitled8.png

BasePlugin.groovy,通过这两个方法,我们可以在 gradle 脚本当中获取 sdk 和 ndk 的路径

untitled9.png

例如: untitled10.png

****untitled11.png

上面给出的只是最常见的 hierarchy 结构,还有 flat 结构,如下图1为 flat 结构,2为 hierarchy 结构。有兴趣的话可以 Google 一下。

untitled12.png

1.2 几个重要的概念

这一小节的出场顺序基本上跟 build.gradle 的顺序一致。

1.2.1 Repository 和 Dependency 如果你只是写 Android 程序,那么依赖问题可能还不是那么的烦人——如果你用 Java 写服务端程序,那可就是一把辛酸一把泪了。 仓库的出现,完美的解决了这个问题,我们在开发时只需要知道依赖的 id 和版本,至于它存放在哪里,我不关心;它又依赖了哪些,构建工具都可以在仓库中帮我们找到并搞定。这一切都是那么自然,要不要来一杯拿铁,让代码构建一会儿?

据说在 Java 发展史上,涌现出非常多的仓库,不过最著名的当然是 Maven 了。Maven 通过 groupId 和 artifactId 来锁定构件,再配置好版本,那么 Maven 仓库就可以最终锁定一个确定版本的构件供你使用了。比如我们开头那个例子, untitled13.png

Maven 就凭这么几句配置就可以帮你搞定 gson-2.4.jar,不仅如此,它还会按照你的设置帮你把 javadoc 和 source 搞定。妈妈再也不用担心我看不到构件的源码了。

那么这个神奇的 Maven 仓库在哪儿呢? Maven Central,中央仓库,是 Maven 仓库的鼻祖,其他的大多数仓库都会对它进行代理,同时根据需求添加自己的特色库房。简单说几个概念:

  • 代理仓库:要租房,去搜房网啊。你要去驾校报名,我是驾校代理,你找我,我去找驾校。具体到这里,还有点儿不一样,一旦有人从代理仓库下载过一次特定得构件,那么这个构件会被代理仓库缓存起来,以后就不需要找被代理的仓库下载了。

  • 私有仓库:中国特色社会主义。走自己的路,你管我啊?公司内部的仓库里面有几个 hosted 的仓库,这些仓库就是我们公司内部特有的,里面的构件也是我 们自己内部的同事上传以后供团队开发使用的。

* 本地仓库:大隐隐于市。跟代理仓库的道理很像,只不过,这个仓库是存放在你自己的硬盘上的。

说起来,Andoid sdk 下面有个 extra 目录,里面的很多依赖也是以Maven 仓库的形式组织的。不过这是 Google 特色嘛,人家牛到不往 Maven 的中央仓库上传,真是没辙。

1.2.2 SourceSets 源码集,这里面主要包含你的各种类型的代码的路径,比如 ‘src/main/java’ 等等。

1.2.3 Properties 前面我们其实也稍稍有提到,这个 properties 其实是 gradle 的属性,在 gradle 源码当中,我们找到 Project.java 这个接口,可以看到:

untitled14.png

不难知道,properties 其实就是一个 map,我们可以在 gradle.properties 当中定义属性,也可以通过 gradle 脚本来定义: untitled15.png

使用方法我们前面已经提到,这里就不多说了。

1.2.4 Project和Task 如果你用过 ant,那么 project 基本上类似于 ant 的 project 标签,task 则类似于 ant 的 target 标签。我们在 build.gradle 当中编写的 untitled16.png

实际上,是调用

untitled17.png

创建了一个 task,并通过 << 来定义这个 task 的行为。我们看到 task 还有如下的重载:

untitled18.png

所以下面的定义也是合法的:

untitled19.png

简单说,project 就是整个构建项目的一个逻辑实体,而 task 就是这个项目的具体任务点。更多地介绍可以参见官网的文档,和 gradle 的源码。


发布构件

发布构件,还是依赖仓库,我们仍然以 Maven 仓库为例,私有仓库多数采用 sonatype。

2.1 UI 发布

如果管理员给你开了这个权限,你会在 UI 上面看到 upload artifact 的 tab,选择你要上传的构件,配置好对应的参数,点击上传即可。

untitled20.png

2.2 使用 Maven 插件

这里的意思是使用 Maven 的 gradle 插件,在构建的过程中直接上传。构建好的构件需要签名,请下载 GPG4WIN (windows),或者 GPGTOOLS(mac),生成自己的 key。 直接上代码:

gradle.properties untitled21.png

build.gradle

untitled22.png

untitled23.png

然后运行 gradle uploadArchives 就可以将打包的 aar 发布到公司的 Maven 仓库当中了。jar包的方式类似,这里就不在列出了。

2.3 使用 Maven 命令

这个可以通过 mvn 在 cmdline 直接发布构件,命令使用说明: untitled24.png

当然这里仍然有个认证的问题,我们需要首先在 maven 的 settings 配置当中加入:

untitled25.png

然后我们就可以使用命令上传了:

untitled26.png

3、插件

3.1 什么是插件

插件其实就是用来让我们偷懒的。如果没有插件,我们想要构建一个 Java 工程,就要自己定义 sourceSets,自己定义 classpath,自己定义构建步骤等等。 简单地说,插件其实就是一组配置和任务的合集。 gradle 插件的存在形式主要由三种,

  • gradle 文件中直接编写,你可以在你的 build.gradle 当中写一个插件来直接引入: untitled27.png

  • buildSrc工程,这个就是在你的工程根目录下面有一个标准的 Groovy 插件工程,目录是 buildSrc,你可以直接引用其中编写的插件。

  • 独立的工程,从结构上跟 buildSrc 工程是一样的,只不过这种需要通过发布到仓库的形式引用。通常我们接触的插件都是这种形式。

详细可以参考:Chapter 61. Writing Custom Plugins

3.2 常见的插件

目前接触到的插件,有下面这么几种:

  • java,构建 java 工程
  • war,发布 war 包用,构建 web 工程会用到
  • groovy,构建 groovy 工程
  • com.android.application,构建 Android app 工程
  • com.android.library,构建 Android library,通常输出 aar
  • sign,签名
  • maven,发布到 maven 仓库
  • org.jetbrains.intellij,构建 intellij 插件工程

3.3 自己动手写一个插件

创建一个普通的 groovy 工程(java 工程也没有关系),创建 src/main/groovy 目录,编写下面的代码: untitled28.png

其中 greettings 就是你的插件 id。

build.gradle

untitled29.png

运行 uploadArchives 发布到本地仓库,那么就可以找到我们自己的插件了,由于当中没有指定 artifactId,那么我们的插件的 artifactId 就是我们的工程名称,比如这里是 deployplugin。 那么我们要怎么引入这个插件呢?

首先要再 buildScript 增加依赖:

untitled30.png

然后: untitled31.png 这样我们的 task “hello” 就被引入了。

Bugly 技术干货系列内容主要涉及移动开发方向,是由 Bugly 邀请腾讯内部各位技术大咖,通过日常工作经验的总结以及感悟撰写而成,内容均属原创,转载请标明出处。

0、讲个故事0.1 Ant,我还真以为你是只蚂蚁

真正开始近距离接触编程其实是在2012年,年底的时候带我的大哥说,咱们这个 app 发布的时候手动构建耗时太久,研究一下 ant 脚本吧。 那个时候连 HashMap 都不知道是啥,可想开发经验几乎为零,一个小小的 ant 脚本看得我真是深深地感受到了这个世界充满的恶意。好在后来硬着头皮搞明白了什么 target 之类的鬼东西,不然就没有然后了。

0.2 Maven,你们真的会读这个单词么 Maven /`meivn/ 接触 Maven,完全是因为读陈雄华的《Spring 实战》,他的源码居然是用 Maven 构建的,结果 Spring 学得一塌糊涂,Maven 我倒是用顺手了。。 跟 Ant 一样,Maven 可以用来构建 Java 工程;跟 Ant 一样,Maven 的配置用 xml 来描述;但,Maven 可以管理依赖,它可以让你做到“想要什么,就是一句话的事儿”。比如我想要个 gson,Maven 说可以,你记下来我带会儿构建的时候给你去取。

真是让你当大爷呢。不过,Maven 这家伙学起来有点儿费劲,很多初学的时候在搭建环境的时候就被搞死了——你以为是因为 Maven 的学习曲线陡峭吗?当然不是,是因为当初 Maven 的中央仓库被 x 了,所以你就天天看着 cannot resovle dependencies 玩就好了。

后来 OSChina 傍上了阿里这个爸爸,就有了 maven.oschina.net。我去年找工作落定之后,想着做点儿什么的时候,发现 maven.oschina.net 估计被阿里爸爸关禁闭,死了几天,现在又活过来了。那又怎样呢,反正中央仓库被 x 的事情也已经成为过去。

0.3 Gradle,你爹是不是 Google!!

13年的时候,我兴奋地跟前面提到的大哥说 Maven 是个好同志的时候,大哥说,Google 推荐用 Gradle……所以,我想 Gradle,你爹是不是 Google……或者至少是个干爹吧。

其实这都不重要了,毕竟 Gradle 实在是好用。比起前面两位的 xml 配置的手段,直接用代码的方式上阵必然是灵活得多。不仅如此,Gradle 居然可以使用 Maven 仓库来管理依赖,就像是一个简易版的 Maven 一样,如果不是看不到 pom 文件,你都还以为你仍然在使用 Maven(当然,由于你在用 Maven 的仓库,所以你自然也是离不开 Maven 的)。哦,你是 Ant 用户啊,那也没关系啊,不信你看:

1、用 Gradle 构建1.1 工程结构

如图所示,这是一个不能更普通的 android 的 gradle 工程了。 根目录下面的 settings.gradle 当中主要是用来 include 子模块的,比如我们这个工程有一个叫做 app 的子模块,那么 settings.gradle 的内容如下:

根目录下面的 build.gradle 包含一些通用的配置,这些配置可以在各个子模块当中使用。 gradle.properties 文件包含的属性,会成为 project 的 properties 的成员,例如我们添加了属性 hello,

hello=Hello Tas! 然后我们在 build.gradle 当中创建 task:

输出地结果是一样的:

local.properties 这个文件在 android 工程当中会遇到,我们通常在其中设置 android 的 sdk 和 ndk 路径。当然,这个 android studio 会帮我们设置好的。为了更清楚地了解这一点,我把 android 的 gradle 插件的部分源码摘录出来:

SDK.groovy,下面的代码主要包含了加载 sdk、ndk 路径的操作。

BasePlugin.groovy,通过这两个方法,我们可以在 gradle 脚本当中获取 sdk 和 ndk 的路径

例如:

上面给出的只是最常见的 hierarchy 结构,还有 flat 结构,如下图1为 flat 结构,2为 hierarchy 结构。有兴趣的话可以 Google 一下。

1.2 几个重要的概念 这一小节的出场顺序基本上跟 build.gradle 的顺序一致。

1.2.1 Repository 和 Dependency 如果你只是写 Android 程序,那么依赖问题可能还不是那么的烦人——如果你用 Java 写服务端程序,那可就是一把辛酸一把泪了。 仓库的出现,完美的解决了这个问题,我们在开发时只需要知道依赖的 id 和版本,至于它存放在哪里,我不关心;它又依赖了哪些,构建工具都可以在仓库中帮我们找到并搞定。这一切都是那么自然,要不要来一杯拿铁,让代码构建一会儿?

据说在 Java 发展史上,涌现出非常多的仓库,不过最著名的当然是 Maven 了。Maven 通过 groupId 和 artifactId 来锁定构件,再配置好版本,那么 Maven 仓库就可以最终锁定一个确定版本的构件供你使用了。比如我们开头那个例子,

Maven 就凭这么几句配置就可以帮你搞定 gson-2.4.jar,不仅如此,它还会按照你的设置帮你把 javadoc 和 source 搞定。妈妈再也不用担心我看不到构件的源码了。

那么这个神奇的 Maven 仓库在哪儿呢? Maven Central,中央仓库,是 Maven 仓库的鼻祖,其他的大多数仓库都会对它进行代理,同时根据需求添加自己的特色库房。简单说几个概念:

代理仓库:要租房,去搜房网啊。你要去驾校报名,我是驾校代理,你找我,我去找驾校。具体到这里,还有点儿不一样,一旦有人从代理仓库下载过一次特定得构件,那么这个构件会被代理仓库缓存起来,以后就不需要找被代理的仓库下载了。 私有仓库:中国特色社会主义。走自己的路,你管我啊?公司内部的仓库里面有几个 hosted 的仓库,这些仓库就是我们公司内部特有的,里面的构件也是我们自己内部的同事上传以后供团队开发使用的。 本地仓库:大隐隐于市。跟代理仓库的道理很像,只不过,这个仓库是存放在你自己的硬盘上的。

说起来,Andoid sdk 下面有个 extra 目录,里面的很多依赖也是以Maven 仓库的形式组织的。不过这是 Google 特色嘛,人家牛到不往 Maven 的中央仓库上传,真是没辙。

1.2.2 SourceSets 源码集,这里面主要包含你的各种类型的代码的路径,比如 ‘src/main/java’ 等等。

1.2.3 Properties 前面我们其实也稍稍有提到,这个 properties 其实是 gradle 的属性,在 gradle 源码当中,我们找到 Project.java 这个接口,可以看到:

不难知道,properties 其实就是一个 map,我们可以在 gradle.properties 当中定义属性,也可以通过 gradle 脚本来定义:

使用方法我们前面已经提到,这里就不多说了。

1.2.4 Project和Task 如果你用过 ant,那么 project 基本上类似于 ant 的 project 标签,task 则类似于 ant 的 target 标签。我们在 build.gradle 当中编写的

实际上,是调用

创建了一个 task,并通过 << 来定义这个 task 的行为。我们看到 task 还有如下的重载:

所以下面的定义也是合法的:

简单说,project 就是整个构建项目的一个逻辑实体,而 task 就是这个项目的具体任务点。更多地介绍可以参见官网的文档,和 gradle 的源码。

2、发布构件 发布构件,还是依赖仓库,我们仍然以 Maven 仓库为例,私有仓库多数采用 sonatype。

2.1 UI 发布 如果管理员给你开了这个权限,你会在 UI 上面看到 upload artifact 的 tab,选择你要上传的构件,配置好对应的参数,点击上传即可。

2.2 使用 Maven 插件 这里的意思是使用 Maven 的 gradle 插件,在构建的过程中直接上传。构建好的构件需要签名,请下载 GPG4WIN (windows),或者 GPGTOOLS(mac),生成自己的 key。 直接上代码:

gradle.properties

build.gradle

然后运行 gradle uploadArchives 就可以将打包的 aar 发布到公司的 Maven 仓库当中了。jar包的方式类似,这里就不在列出了。

2.3 使用 Maven 命令 这个可以通过 mvn 在 cmdline 直接发布构件,命令使用说明:

当然这里仍然有个认证的问题,我们需要首先在 maven 的 settings 配置当中加入:

然后我们就可以使用命令上传了:

3、插件 3.1 什么是插件 插件其实就是用来让我们偷懒的。如果没有插件,我们想要构建一个 Java 工程,就要自己定义 sourceSets,自己定义 classpath,自己定义构建步骤等等。 简单地说,插件其实就是一组配置和任务的合集。 gradle 插件的存在形式主要由三种,

gradle 文件中直接编写,你可以在你的 build.gradle 当中写一个插件来直接引入:

buildSrc工程,这个就是在你的工程根目录下面有一个标准的 Groovy 插件工程,目录是 buildSrc,你可以直接引用其中编写的插件。 独立的工程,从结构上跟 buildSrc 工程是一样的,只不过这种需要通过发布到仓库的形式引用。通常我们接触的插件都是这种形式。

详细可以参考:Chapter 61. Writing Custom Plugins

3.2 常见的插件 目前接触到的插件,有下面这么几种: java,构建 java 工程 war,发布 war 包用,构建 web 工程会用到 groovy,构建 groovy 工程 com.android.application,构建 Android app 工程 com.android.library,构建 Android library,通常输出 aar sign,签名 maven,发布到 maven 仓库 org.jetbrains.intellij,构建 intellij 插件工程

3.3 自己动手写一个插件 创建一个普通的 groovy 工程(java 工程也没有关系),创建 src/main/groovy 目录,编写下面的代码:

其中 greettings 就是你的插件 id。

build.gradle

运行 uploadArchives 发布到本地仓库,那么就可以找到我们自己的插件了,由于当中没有指定 artifactId,那么我们的插件的 artifactId 就是我们的工程名称,比如这里是 deployplugin。 那么我们要怎么引入这个插件呢?

首先要再 buildScript 增加依赖:

然后:

这样我们的 task “hello” 就被引入了。

4、Gradle 运行慢?

用过 Gradle 的朋友多少会感觉到这货有时候会比较慢。我们可以通过下面的三个手段加速你的 Gradle。

  • 不用中央仓库。如果你的 repository 配置的是 mavenCentral,放开它吧,全世界的人都在琢磨着怎么虐它,你就不要瞎掺和了。试试 jCenter。
  • 升级最新的 Gradle 版本。
  • 开启Gradle的电动小马达。在 gradle.properties(眼熟?没错,就是它!!)

里面添加下面的配置: 如果你的任务没有时序要求,那么打开这个选项可以并发处理多个任务,充分利用硬件资源。。嗯,如果你的是单核 CPU。。当我没说。。 org.gradle.parallel=true 这个也可以在命令行通过参数的形式启动,3个小时有效。守护进程可以使编译时间大大缩短 org.gradle.daemon=true 这个看需求吧,Gradle 是运行在 Java 虚拟机上的,这个指定了这个虚拟机的堆内存初始化为256M,最大为1G。如果你内存只有2G,那当我没说。。 org.gradle.jvmargs=-Xms256m -Xmx1024m 当然,建议的方式是在你的用户目录下面的 .gradle/ 下面创建一个 gradle.properties,免得坑你的队友……


如果你觉得内容意犹未尽,如果你想了解更多相关信息,请扫描以下二维码,关注我们的公众账号,可以获取更多技术类干货,还有精彩活动与你分享~

untitled7.png

腾讯 Bugly是一款专为移动开发者打造的质量监控工具,帮助开发者快速,便捷的定位线上应用崩溃的情况以及解决方案。智能合并功能帮助开发同学把每天上报的数千条 Crash 根据根因合并分类,每日日报会列出影响用户数最多的崩溃,精准定位功能帮助开发同学定位到出问题的代码行,实时上报可以在发布后快速的了解应用的质量情况,适配最新的 iOS, Android 官方操作系统,鹅厂的工程师都在使用,快来加入我们吧!