Gradle基础
gradle是一个脚本框架,用来构建android程序的一个脚本,在认识前,我们先以最基础的方式来认识它。
gradle配置
首先在官网下载对应的版本,这里我选择6.6.1-all的版本,下载完后,放到指定的目录,然后配置gradle的环境变量:
- open -e .bash_profile 打开配置文件
- 添加 GRADLE_HOME、PATH
|
|
- source .bash_profile 重置配置文件,以便新 path 生效
- open -e ~/.zshrc 打开另一个配置
- 在最后一行添加 source ~/.bash_profile
- source ~/.zshrc 重置配置文件
配置 zshrc 是因为有的机器 bash_profile 配置不管用,添加这个就行了
- 运行 gradle –version,出现版本号则 Gradle 配置成功
注意:gradle版本和jdk版本需要有对应关系:
这里我安装的是gradle-6.6.1的版本,因此我需要设置JDK8,如果本地安装的jdk版本多个的话,mac会使用最高的版本,先查看本地安装了哪些jdk版本
|
|
输出如下:
|
|
所以默认用的是jdk18,我需要改下环境变量,将jdk改成8:
|
|
再运行:
|
|
上面的jdk配置好后,然后找个文件夹,创建build.gradle,然后输入:
|
|
然后用gradle build.gradle
运行该文件,输出如下:
说明已经成功了。
gradle初始化工程
平时都是直接通过as创建工程,下面通过gradle来创建一个工程:
- 输入
gradle init
来初始化 - 接着会输入相关配置信息:
- 命令行提示选择项目模板
- 命令行提示选择开发语言
- 命令行提示选择脚本语言
- 输入工程名
- 输入包名
最终工程创建如下:
和android studio创建的工程结构是一致的。
Gradle Wrapper
as创建一个工程的时候,默认会有Gradle Wrapper文件夹,它里面会有个wrapper.properties文件夹,配置了gradle相关信息。
Gradle Wrapper 文件的作用就是可以让你的电脑在不安装配置 Gradle 环境的前提下运行 Gradle 项目,你的机器要是没有配 Gradle 环境,那么你 clone gradle 项目下来,执行 init 命令,会根据 gradle-wrapper.properties 文件中声明的 gradle URL 远程路径去下载 gradle 构建工具,cd 进该项目,执行了./gradlew -v,然后下载了对应版本的gradle,如果此时执行的是gradle -v,就用的是本地配置的环境变量中的gradle版本,此时不会去下载。
然后就可以在项目目录下运行 gradle 命令了,不过还是推荐大家在机器配置统一的 Gradle 环境
- gradlew –> linux 平台脚本
- gradlew.bat –> window 平台脚本
- gradle-wrapper.jar –> Gradle 下载、管理相关代码
- gradle-wrapper.properties –> Gradle 下载、管理配置参数
gradle-wrapper.properties 文件中参数详解:
-
distributionUrl –> Gradle 压缩包下载地址
-
zipStoreBase –> 本机存放 Gradle 压缩包主地址
-
zipStorePath –> 本机存放 Gradle 压缩包主路径
- Gradle 压缩包完整的路径是 zipStoreBase + zipStorePath
-
distributionBase –> 本机 Gradle 压缩包解压后主地址
-
distributionPath –> 本机 Gradle 压缩包解压后路径
- Gradle 解压完整的路径是 distributionBase + distributionPath
- distributionBase 的路径是环境 path 中 GRADLE_USER_HOME 的地址
- Windows:C:/用户/你电脑登录的用户名/.gradle/
- MAC:~/.gradle/
- 你 MAC 要是配了 Gradle 环境变量,distributionBase 就是你自己解压缩的 gradle 路径
这几个地址还是要搞清楚的~
关于as如果配置统一的gradle版本,可以看如下截图:
- gradle-wrapper.properties – 使用 wrapper 也就是 AS 来管理 Gradle
- Specifiled location – 使用本地文件,也就是我们自己管理 Gradle,这样每个工程的gradle-wrapper.properties就不起作用了,使用本机配置的gradle版本
gradlw和gradle区别
gradlew实际是一个脚本文件,它是在gradle工程的根目录下面,如果是windows的话,会执行gradlew.bat文件,这个在前面通过命令初始化工程的时候可以看到,其中gradlew脚本会通过gradle/wrapper/gradle-wrapper.jar来使用项目中配置的gradle版本,而gradle命令是使用本机配置的gradle版本。
Gradle中task
task可以认为是gradle中最小执行单元,正是因为有了task,gradle框架才能有序的工作。实际上根据 Gradle 构建项目的流程,是先把所有的 .gradle 脚本执行一遍,编译生成对应的 Gradle、Setting、Project 对象,然后根据我们在构建脚本中设置的构建配置,生成一张 Task 组成的:有向无环图,先来看看这张图:
左边表示的是task之间的依赖关系,右边表示的是项目构建过程中要经过的任务,正是因为有了这些任务,项目才能构建出来。
实际上在gradle构建过程中,会把每一个工程的build.gradle文件会构建成一个project对象,而该project对象由一个个的task组成,比如在构建android项目的时候会有编译java代码的task,编译资源的task等。这些task其实是定义在android插件中,关于插件后面再说。
比如android工程中有很多我们熟悉的task:
可以看到在build这个分组下面有很多熟悉的task,比如有assemble的task,它是用来打包的task。而它们又依赖其它的task,最终一个个的task被执行完,包也打出来了。
比如我在终端上运行
./gradlew build
的时候,会执行一大堆的task:
Task 是完成一类任务,实际上对应的也是一个对象。而 Task 是由无数个 Action 组成的,Action 代表的是一个个函数、方法,每个 Task 都是一堆 Action 按序组成的执行图,就好像我们在 Class 的 main 函数中按照逻辑调用一系列方法一样。
创建task
|
|
运行task
通过./gradlew task名来运行task,运行结果如下:
如果是子模块的话,则通过./gradlew 模块名:task名运行task,运行结果如下:
这是在工程的根目录下build.gradle定义的三个task,然后通过./gradlew hello
命令来执行hello这个task,而其它两个task也输出日志,是因为这几条日志都是在配置阶段输出,关于gradle的生命周期后面会说。那为什么gradlew执行脚本就在根目录下,还需要./来执行该脚本呢?
这是因为在终端里面,需要通过./来获取当前目录,如果不加的话,会导致终端在目录下找不到。
其实task是project的方法,project表示的是模块在初始化阶段会生成,上面使用task hello{},创建的task实际是传入了action的闭包,它是在构建的时候被调用的:
在创建task的时候可以指定其他属性,可以通过task其他的重载方法来创建:
|
|
比如在构建demo1的时候,申明了其它属性,此处的action跟上面写在括号外面是一回事,只不过此处是通过map来创建的,还有一点,map中指定的name不会生效,会被demo1的名字给覆盖了,目前发现没什么作用,执行结果如下:
task.dofirst和dolast
doFirst是task执行前要调用的action,doLast是task执行后要调用的action:
|
|
关于task其它的使用可以在看三方插件的时候再看。
Gradle插件
gradle插件(plugin)是构建项目时候用到的,它其实是由一系列的task组成的,其实android的工程,google已经帮我们实现了android gradle plugin(agp)。google也是遵循了gradle的task依赖法则,构成一个有序无向图。最终去执行一个个的tak。
插件主要通过定义仓库,然后定义插件的名字,比如安卓中依赖agp是通过如下的方式:
|
|
buildscript是rootProject的方法,它声明插件的仓库、添加插件专用闭包
allprojects主要是指所有的模块需要的三方依赖库时候的仓库
像上面定了agp插件3.5.3,然后在app模块中通过apply plugin: 'com.android.application'
来使用这个插件,而app模块选的android{}就是来自于该插件,我们叫他是DSL配置块。
其实像android{}的实现在插件中是通过delegate+闭包实现 DSL 配置块
- 首先把闭包定义好:
|
|
- 定义数据模型
|
|
- 绑定数据
|
|
Gradle构建过程
其实构建过程就是指编译对应的.gradle文件,然后生成对象,最后执行task。 主要分为三个步骤:
- Initialization:初始化阶段,按顺序执行init.gradle,setting.gradle,生成Gradle、Setting、Project对象
- Configuration:配置阶段,按顺序执行 root build.gradle -> 子项目 build.gradle 脚本,生成 Task 执行流程图
- Execution:执行阶段,按照 Task 执行图顺序运行每一个 Task,完成一个个步奏,生成最终 APK 文件
整个流程如下:
通过流程图观察,我们可以在合适的时机监听gradle的构建过程,也就是钩子方法。 下面通过这些钩子方法来获取项目构建各个阶段、任务的耗时情况,在setting.gradle中添加如下代码:
|
|
由于在setting.gradle执行前,Gradle对象其实已经生成,所以通过gradle对象的projectsLoaded回调获取初始化时间。第一次进入到beforeProject的时候记录配置的起始时间,并记录每一个project的配置起始时间,然后在afterProject中算出每一个project配置的时间。然后通过gradle.taskGraph来获取有序无向图,然后通过whenReady方法回调算出总的配置时间。最后通过有序无向图的beforeTask来添加task的开始和结束的执行时间来算出task的执行耗时。最后通过gradle的buildFinish来获取总共的构建时间。
gradle打包速度优化
首先了解下安卓中的variant是什么意思,它表示一个包的变体,其中变体是由productFlavor(产品风味)和buildType(构建类型)组合而成的。其中productFlavor是由维度和渠道构成的,buildType指的是debug还是release,比如维度可以配置价格和渠道的两个维度,而渠道可以配置google、xiaomi等渠道:
|
|
所以上面通过维度和渠道组合后的productFlavor有googleFree、googleVip、xiaomiFree、xiaomiVip四个,再和buildType组合后,就是8个变体,分别是:
- googleFreeDebug
- googleFreeRelease
- googleVipDebug
- googleVipRelease
- xiaomiFreeDebug
- xiaomiFreeRelease
- xiaomiVipDebug
- xiaomiVipRelease
知道了variant后,下面就是了解下打包了,一般通过build Analyze查看build过程中的一些警告,使用该工具需要先进行build,第二个工具是gradle profile工具,一般通过如下命令:
|
|
如果通过adb命令启动主入口的话,需要在清单文件中给主入口加上android:exported="true"
属性,装包完了后,本地会生成一个html文件,该文件会有gradle每个阶段的耗时情况,并且会有每个task的耗时。
再个就是gradle scan工具,一般通过如下命令:
|
|
scan会生成一个在线的网页,第一次使用需要邮箱校验,它的信息比profile更全面,更加智能,其中performance跟profile中效果差不多,它多了timeline选项,表示什么时候执行了什么task,switchs选项表示一些建议,比如缓存没开,会提示off。 其实gradle优化最直接是通过组件化来开发,如果全app编译的话,组件越多,需要的时间越长,所以通过组件化来降低打包时间是最直接的,把一些其他依赖的组件通过aar进行依赖或远程依赖。 参考: Gradle 爬坑指南 – 概念初解、Grovvy 语法、常见 API Gradle 爬坑指南 – 理解 Plugin、Task、构建流程 深度探索 Gradle 自动化构建技术(三、Gradle 核心解密)