Gradle的概述:
在学习Gradle之前,先来了解一下要使用Gradle的原因。
1.项目自动化:
Gmdle是一个构建工具,那么为什么要用构建工具?这就需要先从项目自动化开的讲起。在开发软件时,会面临相似的情况,我们需要用IDE来进行编码,当完成一些功能时会进行编译、单元测试和打包等工作,这些工作都需要开发人员手动来实现。而一般的软件都是迭代式开发的,一个版本接着一个版本,每个版本又可能有很多功能,如果开发每次实现功能时都需要手动进行编译、单元测试和打包等工作,那么显然会非常耗时而且也容易出现问题,因此项目自动化应运而生,它有以下优点:
- 项目自动化可以尽量防止开发手动介入从而节省了开发的时间并减少错误的发生。
- 项目自动化可以自定义有序的步骤来完成代码的编译、测试和打包等工作,使重复的步骤变得简单。
- IDE 可能受到不同操作系统的限制,而自动化构建是不会依赖于特定的操作系统和IDE的,它具有平台无关性。
2.构建工具:
构建工具用于实现项目自动化,是一种可编程的工具,我们可以用代码来控制构建的流程,最终生成可交付的软件。构建工具可以帮助我们创建一个重复的、可靠的、无须平台介入的、不依赖于特定操作系统和IDE的构建。这里以APK的构建过程来举例。
2.1:APK的构建过程:
APK构建的过程主要分为以下几个步骤:
- 通过 AAPT (Android Asset Packaging Tool)打包 res资源文件,比如AndroidManifest.xml和xml布局文件等,将这些xml文件编译为二进制,其中 assets 文件夹和raw文件夹的文件不会被编译为二进制,最终会生成R.java文件和resources.arse文件。
- AIDL工具会将所有aidl接口转化为对应的Java接口。
- 所有Java代码,包括R.java文件和Java接口都会被Java编译器编译成.class文件。
- Dex工具会将第(3)步生成的.class文件、第三方库和其他.class文件都编译成.dex文件。
- 第(4)步编译生成的.dex文件、编译过的资源、无须编译的资源都(如图片等)会被ApkBuilder工具打包成apk文件。
- 使用 Debug Keystore或者Release Keystore对第(5)步生成的apk文件进行签名。
- 如果是对APK正式签名,那么还需要使用zipalign工具对APK进行对齐操作,这样应用运行时会减少内存的开销。
从以上步骤可以看出,APK的构建过程是比较烦琐的,而且这个构建过程又是时常重复的,如果没有构建工具,手动完成构建工作,那么毫无疑问对于开发人员是个折磨,也会产生诸多问题,导致项目开发周期变长。在Gradle出现之前,有三个基于Java的构建工具:Ant、Gant和Maven,它们被应用在Java或者Android开发中。
2.2:Ant:
Ant在这里不是蚂蚁的意思(虽然它的图标是蚂蚁),其指的是Another Neat Tool。它是由James Duncan Davidson(Tomcat最初的开发者)开发的,最初是用来构建Tomcat的。在2000年,Ant成为一个独立的项目并被发布出来。Ant 是由Java编写的构建工具,它的核心代码是由Java编写的,因此具有平台无关性,构建脚本是XML格式的(默认为build.xml),如果熟悉XML,那么Ant就比较容易理解。Ant的构建脚本由三个基本的元素组成,分别是:一个project,多个target和可用的task。Apache Ant有以下缺点:
- Ant无法获取运行时的信息。
- XML作为构建脚本的语言,如果构建逻辑复杂,那么构建脚本就会很长且难以维护。
- Ant需要配合Ivy(一种管理项目依赖工具),否则Ant很难管理依赖。
- Ant在如何组织项目结构方面没有给出任何指导,这导致Ant虽然灵活性高,这样的灵活性导致每个构建脚本都是唯一的而且很难被理解。
2.3:Gant:
Gant是一个基于Ant的构建工具,它在Ant的基础上用Groovy写的 DSL (领域特)语言)。如果用Ant 实现构建,但是不喜欢用XML来编写构建脚本或者现有的XML构脚本很难维护和管理,那么Gant是一个不错的选择。
2.4:Maven:
Maven于2004年发布,它的目标是改进开发人员在使用Ant时面临的一些问题,Maven最初是为了简化Jakarta Turbine项目的构建的,它经历了从Maven到Maven3的发展,Maven参后来者,继承了Ant的项目构建功能,同样采用了XML作为构建脚本的格式。Maven具有依赖管理和项目管理的功能,并且提供了中央仓库,能够帮助我们自动的下载库文件。Maven相比Ant有以下优点:
- Ant是过程式的,开发者需要显式的指定每个目标,以及完成该目标所需要执行的任务。开发者需要重新编写每一个项目的过程,这样会产生大量的重复。而Maven是声明式的,项目的构建过程和过程中的各个阶段都由插件实现,开发者只需要声明项目的基本元素就可以了,这很大程度消除了重复。
- Ant本身是没有依赖管理的,需要配合Ivy来进行依赖管理,而Maven本身就提供了依赖管理。
- Maven使用的是约定而不是配置,它为工程提供了合理的默认行为,项目会知道去哪个目录寻找源码及构建运行时有哪些任务去执行,如果我们项目遵从默认值,那么只需要写几行XML配置脚本就可以了,而Ant是使用配置且没有默认行为的。
Maven也有以下缺点:
- Maven提供了默认的结构和生命周期,这些可能不适合我们的项目需求。
- 为Maven定制的扩展过于烦琐。
- Maven的中央仓库比较混乱,当无法从中央仓库中得到需要的类库时,我们可以手工下载并复制到本地仓库中,也可以建立组织内部的仓库服务器。
- 国内连接Maven的中央仓库比较慢,需要连接国内的Maven镜像仓库。
- Maven缺乏相关的技术文档,不便于使用和理解。
3.Gradle的特性:
Gradle基于JVM,是一个专注于灵活性和性能的开源构建工具,它有约定优于配置的方法,强大的依赖管理,它的构建脚本由Groovy或者kotlin编写,也是Android官方的构建工具。Gradle与竞争对手不同的特性有:
- 轻松的可拓展性:Gradle 有非常良好的拓展性。如果我们想要在多个构建或者项目中分享可重用代码,Gradle的插件会帮助我们实现。将Gradle插件应用在项目中,它会在项目构建过程中能很多帮助:为我们添加项目的依赖的第三方库、添加有用的默认设置和约定(源码位置单元测试代码位置)。其中Android Gradle插件继承Java Gradle插件。
- 采用了Groovy:Ant和Maven的构建脚本是由XML来编写的,如果XML逻辑复杂且内容太多就不容易维护。Gradle可以使用Groovy来构建脚本,Groovy是一种基于JVM的动态语言,它的语法和Java非常相似并兼容Java,因此我们不用担心学习Groovy的成本。Groovy在Jaa的基础上增加了很多动态类型和灵活的特性,相比XML,Gradle更具有表达性和可读性。
- 强大的依赖管理:Gralle 提供了可配置的、可靠的依赖管理方案。一旦依赖的库被下载并存储到本地缓存中,我们的项目就可以使用了。依赖管理很好地实现了在不同的平台和机器上产生相同的构建结果。
- 灵活的约定:Gradle可以为构建项目提供引导和默认值,如果使用这种约定的话,那么Gradle构建脚本的代码量就比较少。相比Ant,Gradle不仅提供了约定,还可以轻松打破约定。
- Gradle Wrapper:Gradle Wrapper是对Gradle的包装,它的作用是简化Gradle本身的下载,安装和构建。比如它会在我们没有安装Gradle的情况下,去下载指定版本的Gradle并进行构建,Gradle的版本很多,所以有可能出现版本兼容的问题,这就需要Gradle Wrapper去统一Gradle的版本,避免开发团队因为Gradle版本不一致而产生问题。
- 可以和其他构建工具集成:Gradle可以和Ant、Maven和Ivy进行集成,比如我们可以把Ant的构建脚本导入Gradle的构建中。
- 底层API:Gradle 显然无法满足所有企业级构建的要求,但是可以通过 Hook Gradle的生命周期来监控和配置构建脚本。
- 社区的支持和推动:Gradle是一个开源的项目,它遵循了Apache License 2.0协议。Gradle的优良特性吸引了很多开发者并形成了Gradle社区,很多开源软件的开发者为Gradle的核心代码做出了共享。
Gradle的基础知识:
为了使用各种开发语言的开发者都能够快速构建项目,专家们开发出了Gradle这个基于Groovy的DSL(Domain Specife Language),DSL意为领域特定语言,只用于某个特定的领域。我们只要按照Groovy的DSL语法来写,就可以轻松构建项目了。task (任务)和action(动作)是Gradle的重要元素。task代表一个独立的原子性操作,比如复制一个文件,编译一次Java代码,这里我们简单地将其定义一个名为hello的任务。一个Task是由一个序列Action组成的,当运行一个Task的时候,这个Task里面的Action序列会按照顺序依次执行。doLast 代表task执行的最后一个action,通俗来讲就是task执行完毕后会回调doLast中的代码。与之相反的是dofirst代表执行的最先执行的操作。
task hello{
doLast{
println 'hello world'
}
}
//doLast方法的快捷版本
task hello<<{
println 'hello world'
}
1.Gradle的任务:
1.1:创建任务:
除了上面的创建任务之外,还可以如下的创建任务:
//直接任务名称创建
task('hello') {
doLast {
println "hello world"
}
}
//任务名称+任务配置创建
//1.创建task时就配置task信息
//2.在task的闭包中设置task信息
task('copy', group: Copy) {
doLast{
println "copy"
}
}
//通过TaskContainer容器创建Task
tasks.create(name:'task') << {
println 'task'
}
1.2:任务依赖:
task之间的依赖关系是通过task name来决定的。我们可以在同一个项目中做task之间的依赖。也可以跨项目进行task的依赖,如果是跨项目的task依赖的话,需要制定task的路径。
//同一个项目
task hello {
doLast {
println 'Hello www.flydean.com!'
}
}
task intro {
dependsOn hello
doLast {
println "I'm flydean"
}
}
//不同项目
project('project-a') {
task task1 {
dependsOn ':project-b:task2'
doLast {
println 'task1'
}
}
}
project('project-b') {
task task2 {
doLast {
println 'task2'
}
}
}
1.3:任务的分组和描述:
Gradle有任务组的概念,可以为任务配置分组和描述,以便于管理任务,拥有良好的可读性。过程是通过group和description来添加分组和描述。
2.Gradle的日志级别:
和Android一样,Gradle也定义了日志级别:
级别 | 用于 |
---|---|
ERROR | 错误消息 |
OUIET | 重要的消息 |
WARNING | 警告消息 |
LIFECYCLE | 进度消息 |
INFO | 信息性消息 |
DEBUG | 调试消息 |
还可以通过开关选项来控制输出的日志级别:
开关选项 | 输出日志级别 |
---|---|
无日志选项 | LIFECYCLE及更高级别 |
-q或者--quiet | QUIET及更高级别 |
-i或者--info | INFO及更高级别 |
-d或者--debug | DEBUG及更高级别 |
3.Gradle命令行:
- gradle -q +任务名称 :运行一个指定的任务。
- gradle -q tasks:获取所有的任务信息。
- gradle 运行的 -x 排除的:排除任务。
- gradle -q help --task 任务:显示任务的帮助信息。
- gradle 任务1 任务2:多任务执行,每个任务执行一次,有先后顺序。
- gradle 缩写:任务名称缩写,使用驼峰命名的任务缩写。
- 更多输入gradle -h查看。
4.补充:
Gradle默认使用build.gradle作为默认的配置文件文件名。如果我们在build.gradle文件中编写代码,那么我们在运行任务的时候,不需要指定build文件名。我们也可以不使用build.gradle,而用另外的文件名来创建build配置文件。例如:我们可以在一个名字为sample.gradle的文件中编写代码。为了运行这个文件中的任务,我们需要在命令行,使用选项 -b 或者 --build-file,后面指定build文件名。但是,我们可以改变项目设置,并且设置项目的默认build文件名。这样设置,我们不需要再使用前面的命令行选项。
Gradle Wrapper:
Gradle Wrapper 被称为Gradle 包装器,是对Gradle的一层包装。为什么需要Gradle
Wrapper呢?比如在一个开发团队中,如果每进来一个成员都需要在计算机中安装Gradle,
那么这时运行Gradle的环境和版本就会给构建结果带来不确定性。针对这个问题,Gradle
提供了一个解决方案,即Gradle Wrapper,它是一个脚本,可以在计算机没有安装Gradle
的情况下运行Gradle构建,并且能够指定Gradle的版本,开发人员可以快速启动并运行
Cradle项目,而不必手动安装,这样就标准化了项目,从而提高了开发效率。Anroid Studio
在新建项目时会自带Gradle Wrapper,这也是我们很少单独去下载安装Gradle的原因。
当使用Gradle Wrapper启动Gradle时,如果指定版本的Gradle没有被下载关联,那会先从Gradle官方仓库将该版本的Gradle下载到用户本地,进行解包并执行批处理文件后续的构建运行都会重用这个解包的运行时安装程序。
1.构建Gradle Wrapper:
首先要确保计算机中配置了Gradle的环境,Gradle 已经内置了Wrapper Task。在项目的根目录中(一般是 build.gradle 的路径下)输入命令 gradle wrapper 即可生成。这时会在项目根目录中生成如下文件,而每个文件的含义如下:
- gradle-wrapper.jar:包含Gradle运行时的逻辑代码。
- gradle-wrapper.properties:负责配置包装器运行时行为的属性文件,用来配置使用哪个版本的Gradlc等属性。
- gradlew:在Linux平台下,用于执行Gralde命令的包装器脚本。
- gradlew.bat:在Windows平台下,用于执行Gralde命令的包装器脚本。
当生成了这些目录与文件后,用户就可以将工程push到远程,当其他用户clone下来后就可以直接进行项目的构建,节省了用户单独下载Gradle的时间,并且可以确保 Gradle版本的一致。
也可以用gradle命令行选项来生成gradle wrapper:
- --gradle-version:用于下载和执行指定的gradle版本。
- --distribution-type:指定下载Gradle发行版的类型,可用选项有bin和all,默认值是 bin,-bin发行版只包含运行时,但不包含源码和文档。
- --gradle-distribution-url:指定下载Gradle发行版的完整URL地址。
- --gradle-distribution-sha256-sum:使用的SHA256散列和验证下载的Gradle发行版。比如使用命令行:gradle wrapper --gradle-version 4.2.1 --distribution-type all,就可以生成版本为4.2.1的包装器,并使用-all发行版。
2.配置Gradle Wrapper:
gradle-wrapper.properties是Gradle Wrapper的属性文件,用来配置Gradle Wrapper,而gradle-wrapper.properties文件中的字段含义如下所示:
- distributionBase:Gradle解包之后存储的主目录。
- distributionPath:distributionBase指定的目录的子目录。而distributionBase+distributionPath就是Gradle解包之后存放的位置。
- distributionUrl:Gradle发行版压缩包的下载地址。
- zipStoreBase:Gradle压缩包存储主目录。
- zipStorePath:zipStoreBase指定目录的子目录。zipStoreBase+zipStorePath就是Gralle压缩包的存放位置。
这里我们需要关注的是distributionUrl字段,如果官方的地址下载不了或者下载缓慢,则可以将这个地址换为其他镜像地址,或者把Gradle发行版压缩包放在服务器上以供下载。
3.使用Gradle Wrapper:
使用Gradle Wrapper不是用Gradle命令,而是用gradlew和gradlew.bat脚本。以Windows为例子,进入项目所在的根目录之后执行gradlew.bat 任务。如果计算机没有Gradle的发行版,则Gradle包装器会将Gradle发行版的压缩包下载到本地并且进行解压。位置是distributionUrl的值,如果之后Gradle属性文件的distributionUrl属性不会更改,就会一直使用本地的Gradle发行版。
4.升级Gradle Wrapper:
升级Gradle Wrapper有两种方式:第一种方式是设置Gradle属性文件的distributionUrl属性,第二种方式是运行wrapper任务。以第二种为例子:想要把当地的Gradle版本升级为5.1.1,则需要运行gradlew wrapper --gradle -version 5.1.1命令即可。
5.自定义Gradle Wrapper:
我们可以对 wrapper task 进行修改,生成自定义配置的 gradle-wrapper.properties。过程是在 build.gradle 构建脚本中通过自定义Wrapper task来配置gradle-wrapper.properties的属性值以达到自定义Gradle Wrapper的效果。
task wrapper(type: Wrapper){
gradleVersion = '7.1'
archiveBase = 'GRADLE_USER_HOME'
archivePath = 'wrapper/dists'
}
Gradle插件的基础:
Gradle本身只是提供了基本的核心功能,而其他的特性是通过插件来实现的,这节主要的是Gradle插件,而不是Android Gradle插件的使用。
1.应用Gradle插件:
应用Gradle插件主要有两个步骤,第一步是解析插件,第二步是把插件应用到项目中,应用插件通过 Project.apply方法来完成。在Gradle中一般有两种类型的插件,分别为脚本插件和对象插件。脚本插件是额外的构建脚本,它会进一步配置构建,可以把它理解为一个普通的build.gradle。对象插件又叫二进制插件,是实现了Plugin接口的类,下面分别介绍如何使用脚本插件和对象插件。
1.1:脚本插件:
脚本插件:脚本插件就是一个普通的xxx.gradle
文件,通过在xxx.gradle定义一系列task,在另一个gradle文件中通过apply from:'xxx.gradle'
引用即可使用xxx.gradle定义的task。
1.2:对象插件:
对象插件是指实现了org.gradle.api.Plugin接口的类。Plugin接口需要实现void apply(T target)
这个方法。该方法中的泛型指的是此Plugin可以应用到的对象,而我们通常是将其应用到Project对象上。对象插件可以分为内部插件和第三方插件。
1.2.1:内部插件:
如果想要应用Java插件可以:
apply plugin:org.gradle.api.plugins.JavaPlugin
因为Gradle默认导入了org.gradle.api.plugins包,所以也可以去掉包名:
apply plugin:JavaPlugin
1.2.2:第三方插件:
第三方的对象插件通常是jar文件,要想让构建脚本知道第三方插件的存在,需要使用buildscrip来设置依赖。
buildscript中的声明是gradle脚本自身需要使用的资源。可以声明的资源包括依赖项、第三方插件、maven仓库地址等。而在build.gradle文件中直接声明的依赖项、仓库地址等信息是项目自身需要的资源。gradle在执行脚本时,会优先执行buildscript代码块中的内容,然后才会执行剩余的build脚本。
buildscript {
repositories {
maven {
uri "地址”
}
dependencies {
classpath "插件的依赖包"
}
}
buildscript的一般格式如上所示:其中 repositories 函数是设置仓库地址的,如果默认提供了该仓库则不用写地址。除了通过上述的方式配置仓库地址外,还有另一种方式设置仓库,就是在项目中创建 buildSrc 子项目。仓库地址配置好后,我们就可以使用该仓库中的插件了,插件依赖使用 classpath 函数来完成,这一步相当于去上面配置的仓库中下载这个插件所在的依赖包,这个依赖包中可能包含很多个插件,有了依赖包我们才是使用其中的插件。最后就是使用apply plugin导入插件了。
1.3:自定义对象插件:
对象插件是指实现了org.gradle.api.Plugin接口的类。想要自定义插件就需要实现void apply(T target)
这个方法。
apply plugin:a
class a implements Plugin<Project>{
@Override
void apply(Project project){
project.task(hello){
doLast{
println "hello"
}
}
}
}
自定义了一个插件a,在apply方法中创建了一个hello任务。
2.包依赖:
包依赖其实是个笼统的概念,包括上面说的插件依赖其实也属于包依赖,这里所说的包依赖是指在实际项目开发中使用的一些第三方的依赖库,这其实不属于编译脚本的范畴,但毕竟也是 Gradle 依赖的一部分。包依赖与脚本依赖本质上都一样,都分为三步:
- 设置包所在的仓库:设置仓库地址跟上面说的一样,都是通过 repositories 函数来实现。
- 下载需要使用的依赖包:声明依赖包需要在对应项目的 build.gradle 的 dependencies 函数中完成。
- 使用该依赖包:通过上面两个步骤之后,我们就可以在 Java 代码或者其他语言的代码中使用依赖包中的代码了。
注意classpath和 implementation,api的区别:
- classpath:一般是添加 buildscript 本身需要运行的东西,buildScript是用来加载gradle脚本自身需要使用的资源,可以声明的资源包括依赖项、第三方插件、maven仓库地址等。某种意义上来说,classpath 声明的依赖,不会编译到最终的 apk 里面。
- implementation、api :在模块中的build.gradle中,给 dependencies 中添加的使应用程序所需要的依赖包,也就是项目运行所需要的东西。其中(implementation:对于使用了该命令编译的依赖,对该项目有依赖的项目将无法访问到使用该命令编译的依赖中的任何程序,也就是将该依赖隐藏在内部,而不对外部公开。api:对比 implementation,不会隐藏。)
3.插件DSL:
Gradle的特性有四种状态,分别是Internal、Incubating、Public、Deprecated,插件DSL属于Incubating状态(即孵化状态)。这也导致插件DSL的特性在将来的Gradle版本中可能会发生变化,直到它不再孵化为止。而新的插件DSL提供了更为简洁,方便的方式来声明插件的依赖关系。
plugins {
id "com.jfrog.bintray" version "0.4.1"
}
plugins {
id 'java'
}
5.Gradle插件的作用和优点:
Gradle插件的作用主要有以下几点:
- 为项目配置依赖。
- 为项目配置约定,比如约定源码的存放位置。
- 为项目添加任务,完成测试、编译和打包等任务。
- 为项目中的核心对象和其他插件的对象添加拓展类型。
使用Gradle插件主要有以下优点。
- 重用和减少维护在多个项目类似的逻辑的开销。
- 更高程度的模块化。
- 封装必要的逻辑,并允许构建脚本尽可能是声明性的。
自定义Gradle插件:
自定义Gradle插件有三种方式:分别是:直接在gradle脚本文件中,在buildSrc过程项目中编写和在独立项目中编写。
1.直接在gradle脚本文件中:
直接在gradle脚本中编写这个方式是最为简单的。打开XXX.gradle
文件,在其中编写一个类实现Plugin接口,然后通过插件类名引用它。
2.在buildSrc过程项目中编写:
除了直接写在某个模块的构建脚本中,我们还可以将插件写在工程根目录下的buildSrc目录下,这样可以在多个模块之间复用该插件。虽然buildSrc是Gradle在项目中配置自定义插件的默认目录,但它并不是标准的Android工程目录,所以使用这种方式需要我们事先手动创建一个buildSrc目录。
2.1:过程:
- 首先New Module,再选择创建 " Java or Kotlin Library " 的 Module。
- 创建好后,打开这个模块的 build.gradle 文件查看。并且在Plugins中添加id 'groovy'这个配置。如果想要给groovy项目指定依赖的JDK版本也可以。
- 然后,因为插件使用的是 groovy 编写,所以还需要在main文件夹下新建
groovy
文件夹(buildSrc/src/main/groovy
)并创建包名文件夹,然后再创建.groovy文件,我们的插件代码在写在其中。 - 插件写好后,我们还需要为该插件创建一个 id,使用这个插件时直接使用这个 id 既可。也可以通过类名引用的方式。
- 通过类名引用插件的需要使用全限定名,也就是需要带上包名,或者可以先导入这个插件类再使用插件。而创建 ID 映射为,在 groovy 同级目录下创建一个 resources\META-INF\gradle-plugins 文件夹,然后在这个文件下创建 ID 映射文件,文件名就是这个插件的 id,后缀为 properties,然后把插件类的全限定名写进去既可。例如我们把上面插件的 ID 命名为 Version,那么我们创建一个 Version.properties 文件,然后编写如下代码:implementation-class=插件类的全限定名 。这个时候我们就可以在别的子项目中使用这个插件。 如:apply plugin: 'Version' 。通过简单的id的方式,我们可以隐藏类名等细节,使的引用更加容易。
apply plugin: com.gary.plugin.CustomPluginInBuildSrc
import com.gary.plugin.CustomPluginInBuildSrc
apply plugin: CustomPluginInBuildSrc
3.在独立项目中编写:
在buildSrc下创建的plugin只能在该工程下的多个模块之间复用代码。如果想要在多个项目之间复用这个插件,我们就需要在一个单独的工程中编写插件,将编译后的jar包上传maven仓库。这里为了不增加复杂度,我们还是在该工程下创建一个模块,而这也是一个完全独立模块,而我们完全可以在一个独立的工程下来编写插件。
独立工程的模块和buildSrc模块结构是一样的。区别在于buildSrc代码在构建是自动会被编译并被引用。而独立模块的则需要编译之后上传到maven仓库中,然后显式地在需要引用的项目中的buildSrcipt中添加对该构件的依赖。
.groovy文件写的插件代码和之前一样只是改了名称,build.gradle则有的不同,因为需要发布到maven,所以增加了发布的代码:apply plugin:'maven'。通过maven插件,我们可以轻松的配置group,version 以及 uploadArchives的相关属性。然后执行./gradlew uploadArchives
这个任务,就可以将构件打包后上传到maven仓库了。执行完毕后就可以在主工程找到repo目录,里面存放的是生成好的jar包与一些文件,而maven-metadata.xml
存放的是配置信息。最后我们通过在主工程的根目录下的build.gradle配置buildScript的classpath依赖,包括buildscript 的repositories和dependencies,依赖完成后我们可以在对应的子工程的build.gradle中进行依赖插件。例如: apply plugin :'插件' 。
Gradle的Android插件:
Gradle有很多插件,为了支持Android项目的构建,Google为Gradle 编写了Android插件,新的Android构建系统就是由Gradle的Android插件组成的,Gradle是一个高级构建工具包,它管理依赖项并允许开发者自定义构建逻辑。Android Studio使用 Gradle wrapper来集成Gradle的Android插件。需要注意的是,Gradle的Android插件也可以独立于AndroidStudio运行。在Android的官方网站中提到了新的Android构建系统主要有以下几个特点:
- 代码和资源易于重用。
- 无论是针对多个APK发行版还是针对不同风格的应用程序,都可以很容易创建应用程序的多个不同版本。
- 易于配置、扩展和自定义构建过程。
- 良好的IDE集成。
1.Android Studio的模块类型和项目视图:
Android Studio中的每个项目包含一个或多个含有源码文件和资源文件的模块,这些模块可以独立构建、测试或调试,一个Android Studio的模块类型可以有以下几种:
1.1:Android应用程序模块:
Android应用程序模块可能依赖于库模块,尽管许多Android应用程序只包含一个应用程序模块,构建系统会将其生成一个APK。
1.2:Android库模块:
Android库模块包含可重用的特定于Android的代码和资源,构建系统会将其生成一个AAR。
1.3:App引擎模块:
App引擎模块包含应用程序引擎集成的代码和资源。
1.4:Java库模块:
Java库模块包含可重用的代码,构建系统会将其生成一个JAR包。
1.5:项目视图:
所有构建文件在Gradle Scripts 层级下显示,这些文件的作用如下。
- 项目build.gradle:配置项目的整体属性,比如指定使用的代码仓库、依赖的Gradle插件版本等。
- 模块 build.gradle:配置当前Module的编译参数。
- gradle-wrapper.properties:用于配置Gradle Wrappe。
- gradle.properties:配置Gradle的编译参数。
- settings.gradle:配置Gradle的多项目管理。
- local.properties:一般用来存放该Android项目的私有属性配置,比如Android项目的SDK路径。
2.Gradle的Android插件类型:
如果apply引入的插件id为com.android.application,说明当前模块是一个应用程序模块,而Gradle的Android插件有以下多个类型:
- 应用程序插件:插件id为com.android.application,会生成一个APK。
- 库插件:插件id为com.android.library,会生成一个AAR,用于提供给其他应用程序模块使用。
- 测试插件:插件id为com.android.test,用于测试其他模块。
- feature 插件:插件id为com.android.feature,是创建Android Instant App时需要用到的插件。
- Instant App插件:插件id为com.android.instantapp,是Android Instant App的入口。
3.build.gradle中的配置:
3.1:Android块:
Android块用于描述该Module构建过程中所用到的所有参数。如:
- compileSdkVersion:配置编译该模块的SDK版本。
- buildToolsVersion:Android构建工具的版本。
3.1.1:dependencies块:
Android块中的dependencies块用于默认的配置,常用的属性如下:
属性 | 描述 |
---|---|
applicationld | 指定 App 的包名 |
minSdkVersion App | 最低支持的SDK 版本 |
targetSdkVersion | 基于哪个 SDK 版本开发 |
versionCode | App内部的版本号,用于控制App升级 |
versionName | App版本名称,也就是发布的版本号 |
testApplicationld | 配置测试App的包名 |
testinstrumentationRunner | 配置单元测试使用的Runner,默认为android.test.InstrumentationTestRunner |
proguardFile | ProGuard混淆所使用的ProGuard配置文件 |
proguardFiles | 同时配置多个 ProGuard配置文件 |
signingConfig | 配置默认的签名信息 |
3.1.2:buildTypes块:
buildTypes块用于配置构建不同类型的APK。当我们新建一个项目时,在Android块中已经默认配置了buildTypes块。在Android Studio 的Terminal中执行 gradlew.bat build 命令,会在该模块的build/outputs/apk目录中生成release和debug的APK,虽然只配置了release,但 release和dsong是默认配置,即使我们不配置也会生成。我们也可以修改默认的release 和detug,甚至可以自定义构建类型。buildTypes块还可以配置很多的属性,如:
属性 | 描述 |
---|---|
applicationldSuffix | 配置 applicationld的后缀 |
debuggable | 表示是否支持断点调试 |
jniDebuggable | 表示是否可以调试NDK代码 |
buildConfigField | 配置不同的开发环境,比如测试环境和正式环境 |
shrinkResources | 是否自动清理未使用的资源,默认值为false |
zipAlignEnabled | 是否开启zipalign优化,提高APK的运行效率 |
proguardFile | ProGuard混淆所使用的ProGuard配置文件 |
proguardFiles | 同时配置多个 ProGuard配置文件 |
signingConfig | 配置默认的签名信息 |
multiDexEnabled | 是否启用自动拆分多个Dex的功能 |
3.1.3:signingConfigs块:
signingConfigs块用于配置签名设置,一般用来配置 release模式,它的常用属性有:
属性 | 描述 |
---|---|
storeFile | 签名证书文件 |
storePassword | 签名证书文件的密码 |
storeType | 签名证书的类型 |
keyAlias | 签名证书中密钥的别名 |
keyPassword | 签名证书中密钥的密码 |
3.1.4:其他配置块:
在android块中除了signingConfigs块,buildTypes块,dependencies块以外,还有其他的配置块,如:
块 | 描述 |
---|---|
sourceSets | 配置目录指向 |
productFlavors | 多个渠道配置 |
lintOptions | Lint配置 |
dexOptions | DEX工具配置 |
adbOptions | adb配置 |
packageingOptions | 打包时的相关配置 |
3.1.5:全局配置:
如果多个module的配置是一样的话,那么可以将这些配置提取出来,也就是使用全局配置。全局配置有很多种方式,这里描述两种。
- 在项目build.gradle中使用ext块,然后在某一个module的build.gradle中使用配置。
- 首先在根目录下创建config.gradle文件来进行配置,然后在项目的build.gradle中添加apply from:"config.gradle"。这样项目的所有module都可以使用在config.gradle中定义的参数。最后在module的build.gradle中使用配置。
3.2: dependencies 块:
dependencies块用于配置该module构建过程中所依赖的所有库。Gradle 插件3.4版本新增了API和implementation来代替compile 配置依赖,其中API与compile是一样的。implementation和API主要有以下区别:
- implementation可以让 module在编译时隐藏自己使用的依赖,但是在运行时这个依赖对所有模块是可见的,而API与compile一样,无法隐藏自己使用的依赖。
- 使用API,如果一个module发生变化,那么这条依赖链上的所有module 都需要重新编译,而使用implemention,只有直接依赖这个module需要重新编译。
4.Android签名文件配置:
在一般公司中,当团队比较小的时候,App的签名信息都是放到项目中的,可能还会上传到GitHub上,这样做很方便。但随着团队人数的增多,这样做的风险会越来越大,因为签名信息是重要的资源,这样就不能将签名上传到GitHub 上了,也就不应该在build.gradle中直接配置签名。针对这个问题,主要有以下两种解决方法:
- 自定义一个签名配置文件。
- 在本地~/.gradle/gradle.properties文件中配置签名信息。
5.Gradle的库依赖:
如果一个Android项目需要引入其他的库,比如:jar,aar和module等等。
5.1:Gradle的本地库依赖:
dependencies {
// 依赖某个jar文件
implementation files('lib/xxx.jar')
// 依赖libs目录下所有以.jar结尾的文件
implementation fileTree(dir: 'lib', includes: ['*.jar'])
// 依赖libs目录下除了xxx.jar以外的所有以.jar结尾的文件
implementation fileTree(dir: 'lib', excludes: ['xxx.jar'], includes: ['*.jar'])
}
5.2:Gradle的本地Module依赖:
当项目中有多个Module时,需要先在settings.gradle中引入Module,然后在模块build.gradle中引入。
5.3:Gradle的远程库依赖:
首先在项目的build.gradle的buildscript块中引入远程库和依赖,然后在模块build.gradle中引入。
6.Gradle的库依赖管理:
随着Gradle依赖的库越来越多,就可能产生一些问题,比如依赖冲突等等,所以需要库依赖的管理。
6.1:前置知识点:
6.1.1:Gradle的依赖传递:
Gradle默认是支持依赖传递的,所以在用到Gradle依赖时一定会涉及到依赖的传递,依赖的传递是,比如:A依赖于B,B依赖于C,那么A依赖于C这样的形式。依赖传递会产生一些问题,比如重复的依赖,依赖的错误等等,我们可以通过transitive来禁止依赖的传递,把它的值设置为false即可。还可以通过下面的代码关闭当前模块的所有库的依赖传递,这样的话就需要手动的添加当前模块的每个库的依赖项,一般不会这么做。
configgurations.all{
transitive=false
}
6.1.2:Gradle的依赖检查:
有了依赖检查,就可以解决依赖产生的问题。依赖检查有很多的方式,常见的有:
- 使用Gradle命令行:使用命令行进入到项目的根目录下面,执行gradle :app:dependencies即可,其中app是新建工程时默认的模块名称。
- 使用Gradle面板:在Android studio中的Gradle面板,找到该模块下面的help目录的dependencies执行。
- 使用Gradle View插件:在Android studio中安装Gradle View插件,然后在View-Tools Windows-Gradle View打开显示依赖项。
6.2:Gradle的依赖冲突:
依赖冲突产生的原因大多是都是库的版本问题,解决的关键有两点:一个是Gradle的依赖检查,另一个是利用Gradle的关键字。transitive是一个关键字,其他的还有:
- force:有时候我们不是要排除某一个库,而是需要强制的使用统一的库版本,force可以强制的设置模块的库版本。
- exclude:有时候需要排除库依赖传递中涉及的库,此时不能靠关闭依赖传递来解决问题,这时候可以使用exclude。