Gradle-Wrapper 的学习笔记

前言

Gradle Wrapper 这个东西其实困扰了我很久,对这个东西一直没搞懂,只知道使用 gradlew 这个命令就可以实现 gradle 的功能,但是实际为什么要这样去操作呢?我还是一知半解的,今天花了点时间大概弄明白了为什么会有 Gradle Wrapper 的存在。

我们去百度搜索 gradle wrapper 相关博客时,一般都是这样说, gradle wrapper 可以在用户没有安装 gradle 的情况下去帮用户去下载 gradle ,然后使用 gradlew 就可以操作 gradle 进行项目的构建了。

然后,我还是没有搞明白。

gradle wrapper 组成&作用

一般情况下,我们使用 AndroidStudio 新建一个工程之后,都会以下几个文件

── gradle
 ── wrapper
        ── gradle-wrapper.jar
        ── gradle-wrapper.properties
── gradlew
── gradlew.bat

它们的作用如下:

  • gradlew 是 shell 脚本(Linux/Mac OS),是一个可执行文件,操作 gradle 命令(windows 是 gradlew.bat)

  • gradle-wrapper.properties 对 gradle 配置,例如 gradle 下载的地址等

  • gradle-wrapper.jar 内部都是用 class 字节码

gradlew 下载 gradle 的源代码就写在这里哦,底层是使用 java 的 HttpURLConnection 实现的,有兴趣可以去浏览一下

上面几个文件是 Andorid 开发者最最熟悉的了,但是一问 wrapper 是干嘛用的又不清楚~

到底 wrapper 是什么东西,我只知道新建一个 Android 工程 AS 就自动帮我创建了这些文件,我根本就不需要去搭理它。

然后我打开官方文档,看看 gradle wrapper 的描述

https://docs.gradle.org/current/userguide/gradle_wrapper.html#sec:adding_wrapper

按照文档的描述主要有以下几个步骤

Gradle_Wrapper_工作流程.png

gradle wrapper 的操作流程

假如有一个用户 A 他的电脑装了 Gradle 并且用 Gradle 来构建 Android 项目,为了方便其它组员开发(统一 gradle 版本号),他通过 Gradle 命令生成了 gradle wrapper ,然后通过 git 将刚才生成的 gradle wrapper 上传到版本库进行管理。

接下来另一个组员 B 他的电脑没有安装 Gradle ,但是呢,他又要去构建这个项目怎么办?这时 gradle wrapper 就起作用了, B 用户git pull 代码之后,他无需去安装 Gradle ,只需要通过 ./gradlew build来构建即可。

这种情况是不是很常见?

接下来,来模拟一下这个流程

用户A开始操作

  • 创建一个文件件 GradleDemo(假设为用户 A 要开发的 Android 项目
mkdir GradleDemo
cd GradleDemo
  • 在终端执行 wrapper 这个任务,生成 gradle wrapper
gradle wrapper --gradle-version 4.4 --distribution-type all
  • 生成的内容如下
── gradle
 ── wrapper
        ── gradle-wrapper.jar
        ── gradle-wrapper.properties
── gradlew
── gradlew.bat

  • 将以上文件通过 git 上传

用户B开始操作

  • 从 git 中拉下用户 A 上传到的代码

  • 操作 gradlew 命令

对于第二点,操作 gradlew 命令(例如: ./gradlew -v),它会有以下几个步骤:

Step 1. 从 gradle 服务器下载 distributions

首先它会检测本地是否有对应 gradle 版本,没有的话就去下载

那去哪里下载呢? 下面的 distributionUrl就是 distributions 的下载地址,这里指定了要下载的版本为 4.4 ,类型为 all

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip

那你肯定会问,all 表示什么,其实通过 Step3 你就知道,all 表示下载的内容包括了 doc,sample,gradle源代码gradle class 编译文件等,

如果你不需要那么多,那你可以将 all 修改为 bin

第一次执行 命令时,它会去下载你配置的 gradle 版本,可能需要翻墙....从下面的日志可以看到,下载的内容会解压到保存到本地目录了


➜ gradle ./gradlew -v

Downloading https://services.gradle.org/distributions/gradle-4.4-all.zip

.......................................................................................................................................................................................................................................................................................

//在这里解压下载回来的 gradle
Unzipping /Users/liaowj/.gradle/wrapper/dists/gradle-4.4-all/4mxuau4c77thx8zlvtz4xiez7/gradle-4.4-all.zip to /Users/liaowj/.gradle/wrapper/dists/gradle-4.4-all/4mxuau4c77thx8zlvtz4xiez7
Set executable permissions for: /Users/liaowj/.gradle/wrapper/dists/gradle-4.4-all/4mxuau4c77thx8zlvtz4xiez7/gradle-4.4/bin/gradle  
  
 
------------------------------------------------------------

Gradle 4.4

------------------------------------------------------------
Build time: 2017-12-06 09:05:06 UTC
Revision: cf7821a6f79f8e2a598df21780e3ff7ce8db2b82
Groovy: 2.4.12
Ant: Apache Ant(TM) version 1.9.9 compiled on February 2 2017
JVM: 1.8.0_111 (Oracle Corporation 25.111-b14)
OS: Mac OS X 10.14 x86_64

Step 2. 保存起来,并且解压 gradle,保存到 Gradle User Home 中,一般是在 $USER_HOME/.gradle/wrapper/dists

例如我笔记本保存 gradle 的路径如下:

/Users/liaowj/.gradle/wrapper/dists/gradle-4.4-all/4mxuau4c77thx8zlvtz4xiez7/gradle-4.4
  • step 3. 然后就可以愉快地使用 gradlew 了

gradle 组成

好了,来看看下载回来的gradle 文件吧~

使用 gradlew 去下载 gradle 之后是一个 zip 包,并且它会帮你解压~

下面就是 gradle-4.4-all.zip 解压的内容了

Snip20190824_7.png

解压内容如下:

gradle-4.4

    bin gradle 可执行文件

    docs 当前 gradle 版本相关的文档都在这~

    lib 里面问很多 jar 包,这些 jar 包都是 gradle 编译好的 class 代码

    samples 是一些 gradle 的示例

    src gradle 的源码就在这里啦

    ... 还有其它...

GRADLE_HOME&GRADLE_USER_HOME

你是不是被这两个东西是不是困扰的许久呢?

看了上面的分析后,gradlew 操作的第一步会将 gradle 下载到指定的目录下

但是我们会有疑问,这个目录能不能我们开发者来指定呢?

先来看看默认的存储目录:

/Users/liaowj/.gradle/wrapper/dists/

我记得 gradlew 是根据 gradle-wrapper.properties 配置文件来决定去哪里下载 gradle 的,那按道理应该会告诉 gradlew 下载之后保存到哪里吧?

#Sat Aug 24 13:13:34 CST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip

推敲:从上面的配置信息再结合默认的存储目录可以看出,gradle 下载的路径其实就是

distributionBase/distributionPath 
也等价于 GRADLE_USER_HOME/wrapper/dists

那就可以推出 GRADLE_USER_HOME 就是 /Users/liaowj/.gradle/

不信?那从代码来验证一下:

在 build.gradle 中写如下代码:

project.getGradle().gradleHomeDir
project.getGradle().gradleUserHomeDir

执行以下命令:

➜  GradleDemo ./gradlew clean
Starting a Gradle Daemon (subsequent builds will be faster)
<-----
> Configure project : 
/Users/liaowj/.gradle/wrapper/dists/gradle-4.4-all/9br9xq1tocpiv8o6njlyu5op1/gradle-4.4
/Users/liaowj/.gradle

那意思就说,我们只要手动去修改 GRADLE_USER_HOME 这个值,那么就可以修改 gradle 的存储目录咯~

那如何去修改呢?跟进去源码可以发现, gradleUserHome 是通过读取环境变量"GRADLE_USER_HOME"来设置

gradleUserHome = System.getenv("GRADLE_USER_HOME");
gradleUserHomeDir设置

所以我们只需要在环境变量中去设置 GRADLE_USER_HOME 就好了

打开vim ~/.bash_profile添加以下配置信息(我在 Mac OS 平台哈~)

GRADLE_USER_HOME=~/Document/.gradle
export GRADLE_USER_HOME

然后我们来验证一下

从下面的输出日志来看,我们已经修改成功了,我们将 /Users/liaowj/.gradle 修改为了 /Users/liaowj/Document/.gradle

➜  GradleDemo ./gradlew clean
Starting a Gradle Daemon (subsequent builds will be faster)
<-----

//因为从新配置了新的存储目录,这里会去下载gradle....

> Configure project : 
/Users/liaowj/Document/.gradle/wrapper/dists/gradle-4.4-all/9br9xq1tocpiv8o6njlyu5op1/gradle-4.4
/Users/liaowj/Document/.gradle

对了,上面的输出日志中,还有一个 project.getGradle().gradleHomeDir 这个又是干嘛的?

这个呢,其实就是你实际下载回来的 gradle 的实际目录了,例如我们工程中 wrapper 中配置的是 4.4 版本那么它的路径如下

/Users/liaowj/Document/.gradle/wrapper/dists/gradle-4.4-all/9br9xq1tocpiv8o6njlyu5op1/gradle-4.4
gradle-4.4-all.zip

那另一个工程中配置的是 3.5 版本那么它的路径如下:

/Users/liaowj/Document/.gradle/wrapper/dists/gradle-3.5-all/9br9xq1tocpiv8o6njlyu5op1/gradle-3.5

也就是说,/Users/liaowj/Document/.gradle 是固定

Gradle版本与插件版本对应表.png

的,后面的就是根据 gradle-wrapper 中的配置然后存储在不同的文件夹下了。

所以从这里就看出为什么 Gradle 官方推荐我们使用 gradle wrapper 去构建工程项目了吧~

Gradle 版本和插件版本

classpath 'com.android.tools.build:gradle:3.4.2'

我之前一直没搞明白这两者的关系,到底哪个插件版本对应使用哪个 Gradle 版本呢?

下面是在 Android Gradle Plugin官网 看到的,日后不懂哪个版本对哪个版本就看看这个吧~

Gradle版本与插件版本对应表.png

Gradle Plugin 简单理解就是将一些公共的功能抽取出来成为插件,增强复用性,插件是基于 gradle 语言去开发的,之所以出现插件版本不匹配对应的 gradle 版本就是因为这个插件使用该 Gradle 版本不具备的 api ,那么你要去升级到对应的版本了,具体就看看上面的表格。

有个 bug, 记录一下~

有一天,我在控制台输入以下命令,它报错了

> gradle build
* What went wrong:

A problem occurred evaluating root project 'GradleDemo'.

> Could not find method google() for arguments [] on repository container of type org.gradle.api.internal.artifacts.dsl.DefaultRepositoryHandler.



* Try:

Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

上面这个这个错误,想必你应该遇过吧~

stackoverflow 可以找到答案,这个错误是你使用的 gradle 版本低于 Gradle 4.x+,所以就会有这个问题呀。

但是我的 gradle-wrapper.properties 是已经是使用了 4.4 版本了啊,为啥还...

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStoreath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip

那问题的根源在哪里?

问题是出现在我在本地的环境变量中配置了 gradle ,并且版本号为 gradle-3.5。

GRADLE_HOME=/Users/liaowj/Library/Android/gradle/gradle-3.5
export PATH=$PATH:$GRADLE_HOME/bin

所以我在终端使用 gradle build的时候使用的是 gradle-3.5 版本,而不是在项目工程下的gradle-wrapper配置的那个版本号。

其实要验证这个问题也很简单,只要执行 gradle -v 就知道当前的 gradle 版本了

➜ GradleDemo gradle -v
------------------------------------------------------------
Gradle 3.5
------------------------------------------------------------

所以,你需要用 gradlew 去构建,那你如果不想修改环境变量,那么你就要去使用

./gradlew build

通过 gradlew 才是真正去用的是 gradle-wrapper 的指定的版本号。

➜ GradleDemo ./gradlew -v
------------------------------------------------------------
Gradle 4.4
------------------------------------------------------------

结论:在工程项目中,尽量使用 gradlew 来构建(Gradle 官方就是这样推荐的~),这样它会跟你项目配置的版本会是一样的,如果你跟我一样项目用的版本跟环境变量配置的 gradle 版本不一样的话就会这种问题啦~

本文是笔者学习之后的总结,方便日后查看学习,有任何不对的地方请指正。

记录于 2019年8月24号

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,158评论 4 370
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,600评论 1 307
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,785评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,655评论 0 220
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,075评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,002评论 1 225
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,146评论 2 318
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,918评论 0 211
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,671评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,838评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,318评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,636评论 3 263
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,343评论 3 244
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,187评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,982评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,126评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,934评论 2 279