Android 构建工具基础教程:Gradle 初学者指南

更新:11-03 名人轶事 我要投稿 纠错 投诉

大家好,感谢邀请,今天来为大家分享一下Android 构建工具基础教程:Gradle 初学者指南的问题,以及和的一些困惑,大家要是还不太明白的话,也没有关系,因为接下来将为大家分享,希望可以帮助到大家,解决大家的问题,下面就开始吧!

1.1 Groovy 环境配置

工欲善其事,必先利其器,先环境后编码。网上有很多为Groovy 开发配置的环境。对于Android程序员来说,使用我们的Android Studio就足够了。

第一步:创建一个 Application 或 Library 工程项目创建完成后,除了build.gradle文件和src/main目录文件夹外,其他都可以删除。

第二步:在 src/main 目录下创建一个 groovy 文件夹在创建的Module项目中的build.gradle文件中添加如下依赖:

应用plugin:"groovy"

依赖项{

编译gradleApi()

编译localGroovy()

}

存储库{

mavenCentral()

}第三步:在 groovy 文件夹中创建 .groovy 文件Android Studio会自动识别groovy文件夹中的.groovy文件。

第四步:配置运行环境单击编辑配置

1.2 Groovy 基本语法

1.2.1 基本数据类型

Groovy 是弱类型语言,但实现仍然与强类型相关。如果类型不正确,仍然会报错。 Groovy 中的基本数据类型有:

byte - 用于表示字节值。例如2.short - 用于表示短整数。例如10。 int - 用于表示整数。例如1234。 long - 用于表示长整数。例如10000090。 float - 用于表示32 位浮点数。例如12.34。 double - 用于表示64 位浮点数,有时可能需要更长的十进制表示形式。例如12.3456565。 char - 这定义了单个字符文字。例如“A”。布尔值- 这表示一个布尔值,可以是true 或false。字符串- 这些是以字符串形式表示的文本。例如,“你好世界”。整数a=4;

浮点b=1.0;

双c=2.1;

字节te=4;

char ch="c";

字符串str="abcd";

1.2.2 变量和方法的声明

在Groovy 中,变量和方法是通过def关键字声明的。

定义a=1

定义b=1.0

def str="你好世界"

定义输出(){

打印“你好世界”

}Groovy 和脚本类似,所以很多都可以省略:

语句后面的分号可以省略。变量的类型可以省略。方法的返回值可以省略。 return 语句可以省略。方法的声明Groovy也是一种JVM语言,因此在语法上与Java有很多相似之处。这样声明方法时的格式也比较随意,所以作为Android程序员,我们可以选择更接近Java语法格式的风格。

def 方法名() {

打印("HelloWorld")

}

def int sum2Number(a, b) {

返回a + b

}

def sum(a, b) {

返回a+b

}

int 添加(a,b) {

返回a + b

}

1.2.3 循环

Groovy 中的循环控制语句与Java 中的类似。共有三种类型:

for 语句while 语句for-in 语句for(int i=0;i 9; i++) {

打印(i)

}

整数我=0

而(i++ 9){

打印(i)

}

for(int j in 1.9) {

打印(j)

同样,循环也有循环控制语句:

break:break 语句用于结束循环和switch 语句内的控制流。 continue:结束本次循环,进入下一个循环,仅限于while和for循环。 for(int i=0;i 9; i++) {

打印(i)

继续

}

for(int j in 1.9) {

打印(j)

休息

1.2.4 条件判断语句

Groovy中的条件判断语句与Java中的条件判断语句类似,包括:

ifif.elseif.else if.elseswitch 示例将不再演示。语法与Java相同。

上面对Groovy的基本介绍中,它在形式上与Java语言非常相似,没有太多变化。对于Java和Android程序员来说应该很容易上手。

1.3 Groovy 中的集合

Java中的集合主要包括:List和Map派生的类。 Groovy 中也存在集合。名称与Java中的相同,但形式发生了变化,使它们更简单,更容易操作。 Groovy 中的集合:

列表:也称为列表,列表是用于存储数据项集合的结构。 Map:也称为映射,映射(也称为关联数组、字典、表和哈希)是对象引用的无序集合。

1.3.1 List 列表

基本语法:列表列表使用[ ] 声明,并通过索引进行区分。

def listEmpty=[] //空列表

def list=[1,2,3,4,5] //整数值列表

def listInt=[1,[2,3],4,5] //List嵌套列表

def listString=["andoter","note"] //字符串列表

def listNone=["andoter",1,4] //异构对象列表中的方法:

boolean add(E e)void add(int index, E element)boolean addAll(Collection? extends Ec)void clear()boolean contains(Object o)Iteratoriterator()Object[] toArray()int lastIndexOf(Object o)E set (int index, E element) 这些方法与Java中的类似。打开对应的类型查看后,发现通过def声明的列表竟然在java.util.List下面。

def listEmpty=[] //空列表

def list=[1,2,3,4,5] //整数值列表

def listInt=[1,[2,3],4,5] //List嵌套列表

def listString=["andoter","note"] //字符串列表

def listNone=["andoter",1,4] //异构对象列表

listEmpty.add(1)

列表为空6

println(listEmpty.size())

列表.clear()

println(listInt.contains([2,3]))

println(listString.lastIndexOf("注释"))

Println(listNone.indexOf(1)) 需要注意的是,List 也存在于groovyjarjarantlr.collections.List 包下,所以使用时需要注意。

关于List的遍历,我们可以参考Java中的Iterator接口来遍历,或者使用Groovy系统提供的each方法来遍历。 Groovy中提供了DefaultGroovyMethods类,它定义了很多快捷方法:

abs:计算绝对值addAll(Collection)each:遍历eachWithIndex:带索引遍历grep:将提取满足条件的元素形成listevery:如果所有元素都满足条件则返回true,否则返回falseany:true只要有满足条件的元素,否则falsejoin:使用指定字符连接集合中elementsort:按照指定条件排序find:查找集合中符合条件的‘第一个’元素FindAll:查找‘全部’ " 集合中满足条件的元素使用的方法有很多,可以参考源码查看。

def listString=["andoter","注释"]

列表字符串.each {

打印(它)

}

列表字符串.each {

值-println(值)

}

1.3.2 Map 映射

Map集合中的元素通过键值访问。 Map 中使用的键可以属于任何类。当我们插入Map集合时,需要两个值:key和value。

def 地图空=[:]

def mapString=["姓名":"andoter","电子邮件":"andoter0504@gmail.com"]

def mapInt=["name" : 123, "age" : 26] 映射中的方法:

voidclear()boolean containsValue(Object value)Map.Entryeldest()SetentrySet()void forEach(BiConsumer? super K, super Vaction)V get(Object key)SetkeySet()Collectionvalues() 总体方法与Map相同在Java中同样如此。

def 地图空=[:]

def mapString=["姓名":"andoter","电子邮件":"andoter0504@gmail.com"]

def mapInt=["姓名": 123,"年龄": 26]

mapEmpty.put("名称","andoter")

mapEmpty.values()

mapString.get("名称")

mapInt.containsValue("123")

mapString.each {key, value -if(key==null || key.length()==0) {

println("空对象")

}

if(key.equals("name")){

println(键+"="+值)

}别的{

println(键+ ":" + 值)

}

}

1.4 Groovy 中的 IO 操作

Java提供了一系列用于文件操作的java.io.*方法,这些方法在Groovy中也适用。 Groovy 增强了Java 提供的方法,使它们使用起来更加方便。

def file=new File("/Users/dengshiwei/WorkProject/GradlePlugin/groovydemo/src/main/groovy/TestGroovy.groovy")

if (文件.exists()) {

文件.eachLine {

行-println(行)

}

} 别的{

print("文件不存在")

这是一个简单的例子。更多信息请参考官方API接口。

1.5 闭包

闭包是Groovy 中的一个非常重要的功能。它使Groovy 语言更加灵活。在Gradle项目的构建中,广泛使用的是DSL,因此掌握闭包的使用对于掌握Android项目的构建非常重要。影响。

1.5.1 闭包的定义

闭包定义格式:

{

参数语句

}它在形式上与Lambda表达式非常相似,所以对于熟悉Lambda表达式的同学来说闭包上手非常容易。如果闭包没有定义参数,则意味着有一个参数it,与Java中的类似。假设你的闭包不需要接受参数,它仍然会生成一个隐式参数它,但它的值为null,也就是说,一个闭包至少包含一个参数。

无参数的闭包def 关闭={

println("无参数")

}一个参数的闭包defclosureOneParameters={

key-println(key)

}两个参数的闭包defclosure2Parameter={

键,值-if (键==1) {

键=键+ 1

println(键+ ":" + 值)

} 否则如果(键==2)

println(键+ ":" + 值)

}

1.5.2 闭包的特性

闭包的引入使得Groovy语言更加简单、方便。例如,作为函数的最后一个参数,可以在函数中单独编写一个闭包。本节介绍闭包的常见使用形式。

封闭特性:

闭包可以访问外部变量,但方法不能访问外部变量。闭包可以包含代码逻辑。闭包中的最后一行语句表示闭包的返回值。无论语句是否使用return 关键字命名,如果最后一行语句没有输入任何类型,则闭包将返回null。闭包的参数声明写在“-”符号之前。调用闭包的标准方式是:闭包名称.call(闭包参数)。当闭包用作闭包或方法的最后一个参数时,闭包的一些快捷方式。闭包可以从参数括号中提取出来并附加在末尾。如果闭包是唯一的参数,则闭包或方法参数周围的括号也可以省略。对于有多个闭包参数的,只要在参数声明的末尾,就可以按照上面的方式省略。闭包作为函数参数当闭包用作函数参数时,其使用方式与普通变量参数相同。

def checkKey={

地图-if (map.size()==0) {

println("参数为空或为空")

}

println(地图)

}

def enqueue(键, 值, 闭包) {

默认地图=[:]

映射.put(键,值)

关闭(地图)

}

enqueue(1, 2, checkKey) 通常,当函数有闭包作为参数时,闭包将被放置在最后一个参数的位置。当闭包作为最后一个参数的时候,闭包可以抽离到函数体之外,提高函数的简洁性。关于 Groovy 比较好的文章深入了解Android的GradleGroovy高级函数、闭包和类

2. Gradle DSL 语言

上面我们简单学习了Groovy语言,接下来就是学习Gradle DSL语言。 Gradle 是在Android Studio 中构建项目的一种新方式。

Gradle 是一款开源自动化构建工具,提供更高的灵活性和体验。 Gradle 脚本是用Groovy 或Kotlin 编写的。官方文档

2.1 基本概念

Gradle是脚本配置,所以执行的时候需要对应对应的类型。 Gradle中有三种类型:

脚本类型委托的实例Build scriptProjectInit scriptGradleSettings scriptSettingsGradle 是围绕项目Project 展开的,所以Project 是我们最重要的接口。通过Project接口,我们可以获取整个Gradle的属性。通常我们项目在Project模式下的结构是:

app #Android应用目录

app.iml

build #构建输出目录

build.gradle #构建脚本

libs #so相关库

proguard-rules.pro #proguard混淆配置

src #源码、资源等

建造

中间体

build.gradle #项目构建文件

梯度

包装纸

gradle.properties #gradle配置

gradlew #gradle 包装Linux shell 脚本

gradlew.bat

LibSqlite.iml

local.properties #配置Androod SDK位置文件

settings.gradle #项目配置

2.2 Project

2.2.1 生命周期 Lifecycle

项目和build.gradle文件是一一对应的关系。在初始化脚本构建过程中,Gradle 为每个项目创建Project 对象。步骤如下:

初始化阶段在初始化阶段,构建工具会根据每个build.gradle文件创建每个项目对应的Project,同时还会执行项目根目录下的settings.gradle来分析需要参与编译的项目。

比如我们常见的settings.gradle配置文件:

include ":app", ":groovydemo" 指定需要编译的项目。

配置阶段配置阶段为每个项目创建和配置任务。配置阶段将加载所有参与项目的build.gradle文件,将每个build.gradle文件转换为Gradle Project对象,分析依赖关系并下载依赖关系。

执行阶段Gradle根据Task之间的依赖关系以及Task之间的顺序来决定需要执行哪些Task。

2.2.2 Task

任务是Gradle中最小的执行单元。所有的构建、编译、打包、调试、测试等都执行特定的任务。一个项目可以有多个任务,并且任务可以相互依赖。例如,我有两个任务,TaskA 和TaskB。我指定TaskA依赖于TaskB,然后执行TaskA。这时会先执行TaskB,执行完TaskB后再执行TaskA。

同时我们还可以自定义Task,查找Task是否存在。从编程的角度来看,Task也是一个具有核心方法的类:

String getName():获取任务名称Project getProject():获取任务所在的Project 对象ListgetActions():获取ActionTaskDependency getTaskDependency():获取任务依赖项Task dependentOn(Object. var1):任务依赖项function void onlyIf(Closure var1)TaskState getState(): 获取任务的状态Task doFirst(Closure var1): 任务先执行.Task doLast(Closure var1): 任务后执行.String getDescription() :获取任务描述String getGroup():获取任务组

1. 任务的创建

任务是Gradle中最小的基本执行单元。创建任务有多种方式:

Project.task(字符串名称)Project.task(字符串名称, 闭包configureClosure)Project.task(Mapargs, 字符串名称, 闭包configureClosure)Project.task(Mapargs, 字符串名称)TaskContainer.create(字符串名称)TaskContainer.create(Mapoptions )前三个是根据Project提供的任务重载方法创建的。这里我们需要重点关注里面的Map参数。 Map参数选项用于控制Task的创建和属性。

配置项说明默认值type 任务创建类型DefaultTaskoverwrite 是否覆盖已有任务falsedependsOn 添加任务依赖[] action 添加任务中的动作nulldescription 任务描述nullgroup 配置任务所属组null 第一种方法:创建任务直接用任务名称

任务copyTask=任务("copyTask")

copyTask.description="复制任务"

copyTask.group="自定义"

复制任务.doLast {

print("复制任务创建")

}这个方法和Java中创建对象的方式非常相似。该方法的本质是调用Project类中的task(String name)方法来创建对象。

第二种方法:任务+闭包方法

任务taskClosure=project.task("taskClosure"){

print("任务结束")

}这种写法可以在闭包为最后一个参数时抽取到外部写法。上面的写法还可以简化:

任务任务Clousre {

print("任务结束")

}这种形式在.gradle脚本文件中被大量使用,所以每个人也应该写这个!

第三种:任务+地图

任务mapTask=project.task(dependsOn: copyTask,description:"mapTask",group:"mapTask",

"地图任务"){

println("地图任务创建")

这里我们使用Map来进行Task的一些设置。这里我们也可以用同样的方式单独设置。

类型4:TaskContainer创建任务

project.tasks.create("TaskContainer") {

描述“任务容器”

组“任务容器”

做最后{

println("任务容器")

}

}在上面的演示例子中,我们也介绍了任务的分组和描述的使用,可以在 Gradle Pojects 栏中进行查看任务的分组和描述。
2. 任务之间的关系
dependsOn(Task task) 任务依赖,通过 dependsOn 可以建立任务之间的执行依赖关系,先执行依赖的任务。 def name = "Hello World from" task checkName { if (name.length() >0){ name = name.replace("from", "") } } task printName() { println(name) } printName.dependsOn(checkName)mustRunAfter(Task task) 必须在添加的任务之后执行。 def name = "Hello World from" task checkName { if (name.length() >0){ name = name.concat(" China") } } task printName() { println(name) } printName.mustRunAfter(checkName)
3 任务类型
在 1 节中,我们提到了创建任务时可以通过 Map 配置任务的依赖属性关系,里面涉及到 任务类型(type),默认值是 DefaultTask 类型,关于 type,我的理解是 Groovy 系统的 Task 类型,我们查看官方文档,可以看到有很多 Task 类的子类,这些应该都可以作为 type 值进行设置?那么常见的任务类型有哪些呢? 官方 Task API Copy 类型 Task,Copy 任务的方法:eachFile:遍历文件exclude(Closure excludeSpec):去除包含的内容filter(Closure closure):过滤from(Object... sourcePaths):源目录include(Closure includeSpec):包含内容into(Object destDir):目标目录rename(Closure closure):重命名with(CopySpec... sourceSpecs)通过 Copy 任务,我们可以更方面实现文件的拷贝复制类操作,因为它提供了一些封装好的方法,不需要我们在通过 File 的操作进行。常见的就是创建的 makeJar 任务,拷贝系统编译的 jar 包到指定目录。 task makeJar(type: Copy) { def jarName = "SensorsAnalytics-Android-SDK-Release-" + version + ".jar"; delete "build/libs/" + jarName from("build/intermediates/bundles/release/") into("build/libs/") include("classes.jar") rename("classes.jar", jarName) }makeJar 的任务执行步骤:首先设置 type 是 Copy 类型任务,定义拷贝目标的 jar 名称,接着删除 目标目录已存在的 jar 文件,from 从源目录拷贝到 into 的目标目录,包含 include 的 classes.jar,最后给文件重命名。 在 Android Studio 的 2.X 版本中自动生成的 jar 包路径在:build/intermediates/bundles/release/ 目录,在 3.X 中目录:build/intermediates/packaged-classes/release/。 Jar 类型 Tasktask makeJar(type: Jar, dependsOn: build) { delete "build/libs/sensorsdata.jar" from("build/intermediates/classes/release") into("build/libs/") exclude("android/", "androidx/","BuildConfig.class", "R.class") exclude { it.name.startsWith("R$") } }Jar 任务的作用就是打包 jar 包,比如我们通过 from 指定工程目录中的源码进行打包,这样我们就可以实现高度的定制化,不像通过 Copy 任务复制系统根据整个目录生成的 Jar 包一样。比如下面的 task 生成 Jar 包。 task makeJars(type: org.gradle.jvm.tasks.Jar) { from(android.sourceSets.main.java.srcDirs) }在 Gradle 中提供了很多常见的任务: Gradle System Tasks

2.3. 常见脚本块

Gradle 支持 Groovy 语言进行编写,非常灵活支持各种插件。比如你想在脚本中使用一些第三方的插件、类库等,就需要自己手动添加对这些插件、类库的引用,而这些插件、类库又不是直接服务于项目的,而是支持其它 build 脚本的运行,所以你应当将这部分的引用放置在 buildscript 代码块中。 gradle 在执行脚本时,会优先执行buildscript代码块中的内容,然后才会执行剩余的build脚本。所以需要我们了解常见的脚本块配置。

2.3.1 allprojects { }

配置整个 Project 和子项目的配置。 allprojects { repositories { google() jcenter() } }比如我们在 allprojects 内部定义的 Task 任务,就会用于根目录和子项目。比如下面的例子,我们执行:./gradlew print 任务,打印的结果如下: allprojects { task print { println project.name } } 输出结果: GradlePlugin(根目录) app groovydemo sensorsdatalibrary

2.3.2 buildscript { }

buildscript 中的声明是 gradle 脚本自身需要使用的资源。 buildscript { repositories { google() jcenter() mavenCentral() } //格式为-->group:module:version dependencies { classpath "com.android.tools.build:gradle:3.1.2" classpath "com.qihoo360.replugin:replugin-plugin-gradle:2.2.4" } }

2.3.3 configurations { }

配置整个 Project 的 dependency 属性,与之对应的是 ConfigurationContainer,在 Project 项目中可以通过以下方法获取 ConfigurationContainer 对象: Project.getConfigurations()configurations通常使用最多的就是去除依赖,比如我们添加的依赖中也依赖某个库,这种间接依赖的冲突,transitive dependencies 被称为依赖的依赖, 称为“间接依赖”比较合适。 configurations { compile.exclude module: "commons" all*.exclude group: "org.gradle.test.excludes", module: "reports" }

2.3.4 dependencies { }

配置项目的依赖库,与之对应的是 DependencyHandler 类。 在 dependencies{} 脚本块中有不同的依赖方式,这里在 Android Studio 的 2.X 版本与 3.X 版本中差别还是挺大的,Android Studio3.0 中,compile 依赖关系已被弃用,被 implementation 和 api 替代,provided 被 compile only 替代,apk 被 runtime only 替代。为了比较方便,前面写是 3.X 版本,括号是 2.X。 implementation:依赖的库只能在本项目使用,外部无法使用。比如我在一个 libiary 中使用 implementation 依赖了 gson 库,然后我的主项目依赖了 libiary,那么,我的主项目就无法访问 gson 库中的方法。这样的好处是编译速度会加快,推荐使用 implementation 的方式去依赖,如果你需要提供给外部访问,那么就使用 api 依赖即可api(compile):使用该方式依赖的库将会参与编译和打包testImplementation(testCompile):只在单元测试代码的编译以及最终打包测试 Apk 时有效debugImplementation(debugCompile):只在 debug 模式的编译和最终的 debug Apk 打包时有效releaseImplementation(releaseCompile):仅仅针对 Release 模式的编译和最终的 Release Apk 打包compileOnly(provided):只在编译时有效,不会参与打包,可以在自己的moudle中使用该方式依赖。比如 com.android.support,gson 这些使用者常用的库,避免冲突。runtimeOnly(apk):只在生成 Apk 的时候参与打包,编译时不会参与,很少用。下面是一些常见的依赖使用方式: apply plugin: "java" //so that we can use "compile", "testCompile" for dependencies dependencies { //for dependencies found in artifact repositories you can use //the group:name:version notation compile "commons-lang:commons-lang:2.6" testCompile "org.mockito:mockito:1.9.0-rc1" //map-style notation: compile group: "com.google.code.guice", name: "guice", version: "1.0" //declaring arbitrary files as dependencies compile files("hibernate.jar", "libs/spring.jar") //putting all jars from "libs" onto compile classpath compile fileTree("libs") }在实际项目开发中,我们会引入很多第三方开源库,自然就会造成依赖冲突,这里就涉及到在 dependencies 提供的配置字段: force = true:即使在有依赖库版本冲突的情况下坚持使用被标注的这个依赖库版本transitive = true:依赖的依赖是否可用,举个例子,使用的三方库中可能也依赖别的库,我们称之为“间接依赖”exclude:用于排除指定版本库,通常用于排除冲突依赖库dependencies { compile("com.sensorsdata.analytics.android:SensorsAnalyticsSDK:2.0.2") { //强制使用我们依赖的 2.0.2 版本库 force = true //剔除间接依赖的库,可以通过这三种方式,后面再讲解自定义插件的时候就能看懂这三种方式了。 exclude module: "cglib" //by artifact name exclude group: "org.jmock" //by group exclude group: "org.unwanted", module: "iAmBuggy" //by both name and group //禁用所有的间接依赖库 transitive = false } }

2.3.5 repositories { }

配置 Project 项目所需的仓库地址,Gradle 必须知道从哪里下载外部依赖,这是由仓库配置来指定的,比如 google()、jcenter() 或 mavenCentral()。通常在 buildscript 脚本块中也能看到配置的 repositories 属性,buildscript 中的声明是 gradle 脚本自身需要使用的资源,可以声明的资源包括依赖项、 第三方插件、 maven 仓库地址等。而在 build.gradle 文件中直接声明的依赖项、仓库地址等信息是项目自身需要的资源。 repositories { //Maven本地仓库,寻找本地仓库的逻辑与Maven相同 mavenLocal() //Maven中心仓库 mavenCentral() //JCenter仓库 jcenter() //其它Maven远程仓库 maven { //可以指定身份验证信息 credentials { username "user" password "password" } url "http://repo.mycompany.com/maven2" //如果上面的URL找不到构件,则在下面找 artifactUrls "http://repo.mycompany.com/jars" } //Ivy远程仓库 ivy { url "http://repo.mycompany.com/repo" } //Ivy本地仓库 ivy { url "../local-repo" } //扁平布局的文件系统仓库 flatDir { dirs "lib" } flatDir { dirs "lib1", "lib2" } }

2.3.6 sourceSets { }

配置项目的源码目录结构。 sourceSets { main { java { srcDirs = ["src/java"] } resources { srcDirs = ["src/resources"] } } }

2.3.7 subprojects { }

用于配置子项目的脚本块。比如我们在 subprojects 中配置 print 任务,则只会作用于子目录。 subprojects { task print { println project.name } }

输出结果: app groovydemo sensorsdatalibrary

2.3.8 publishing { }

用于发布构建。 publishing { publications { myPublication(MavenPublication) { from components.java artifact sourceJar pom { name = "Demo" description = "A demonstration of Maven POM customization" url = "http://www.example.com/project" licenses { license { name = "The Apache License, Version 2.0" url = "http://www.apache.org/licenses/LICENSE-2.0.txt" } } developers { developer { id = "johnd" name = "John Doe" email = "john.doe@example.com" } } scm { connection = "scm:svn:http://subversion.example.com/svn/project/trunk/" developerConnection = "scm:svn:https://subversion.example.com/svn/project/trunk/" url = "http://subversion.example.com/svn/project/trunk/" } } } } }

2.4 Gradle 插件

通过使用插件可以扩展项目的功能,帮助我们做很多任务,比如编译、打包,Gradle 插件可以分为两类: 二进制插件:继承 org.gradle.api.Plugin 接口实现的插件脚本插件:直接在 build.gradle 配置文件

2.4.1 二进制插件

常见的二进制插件 com.android.application,这里的 ‘com.android.application’ 就是插件的 plugin id,二进制插件的使用形式: apply plugin: plugin idapply plugin : "java"

2.4.2 脚本插件

通常脚本插件用于本地的配置存储,使用格式: apply from: ‘fileName’// config.gradle rootProject.ext { android = [ compileSdkVersion : 28, buildToolsVersion : "28.0.0", applicationId : "sw.andoter.com.gradleplugindemo", minSdkVersion : 18, targetSdkVersion : 28, versionCode : 1, versionName : "1.0" ] sdkVersion = 13 apkPath = [ apkPath : "/Users/dengshiwei/Desktop/*.apk" ] } apply from: "../config.gradle"

2.5 Gradle 自定义插件

在基于 Gradle 的项目构建中插件的使用非常常见,比如 com.android.application、com.android.library 等,如何自定义自己的插件呢?在 Gradle 中提供了 Plugin 接口用于自定义插件,本小节只做简单介绍自定义插件的步骤: 创建一个 module,什么样的都可以,不管是 Phone&Tablet Module 或 Android Librarty 都可以,然后只留下 src/main 和 build.gradle,其他的文件全部删除。 在main 目录下创建 groovy 文件夹,然后在 groovy 目录下就可以创建我们的包名和 groovy 文件了,记得后缀要以 .groovy 结尾。在这个文件中引入创建的包名,然后写一个 Class 继承于 Plugin< Project >并重写 apply 方法。 class MyPlugin implements Plugin{ @Override void apply(Project project) { System.out.println("-----------插件开始-----------") System.out.println("---这是我们的自定义插件---") System.out.println("-----------插件结束-----------") } }在 main 目录下创建 resources文件夹,继续在 resources 下创建 META-INF 文件夹,继续在 META-INF 文件夹下创建 gradle-plugins 文件夹,最后在 gradle-plugins 文件夹下创建一个 xxx.properties 文件,注意:这个 xxx 就是在 app 下的 build.gradle 中引入时的名字,例如:apply plugin: ‘xxx’。在文件中写 implementation-class=implementation-class=com.andoter.customplugin.MyPlugin。 implementation-class=com.andoter.customplugin.MyPlugin打开 build.gradle 删除里面所有的内容。然后格式按这个写,uploadArchives 是上传到 maven 库,然后执行 uploadArchives 这个 task,就将我们的这个插件打包上传到了本地 maven 中,可以去本地的 Maven 库中查看。 apply plugin: "groovy" apply plugin: "maven" dependencies { compile gradleApi() compile localGroovy() } repositories { mavenCentral() } group = "com.andoter.customplugin" version = "1.0" uploadArchives { repositories { mavenDeployer { repository(url: uri("../repo")) } } }在上面的实现中,我们也可以把 group、version 字段配置在内部: uploadArchives { repositories { mavenDeployer { repository(url: uri("../repo")) pom.groupId = "com.andoter.customplugin" pom.artifactId = "groovydemo" pom.version = "1.0" } } }应用 gradle 插件:在项目下的 build.gradle(也可以在 module 中)中的 repositories 模块中定义本地 maven 库地址。在 dependencies 模块中引入我们的插件的路径。 // 根目录 .gradle 文件配置插件的地址 buildscript { repositories { google() jcenter() mavenCentral() maven { url "./repo" } } //格式为-->group:module:version dependencies { classpath "com.android.tools.build:gradle:3.1.2" classpath "com.andoter.customplugin:groovydemo:1.0" } } // 子项目使用插件 apply plugin: "com.andoter.customplugin"这样就完成一个自定义插件的使用步骤,自定义插件的核心开发一个什么样的插件,比如结合 Transform 开发一个编译时框架。

3. Android DSL

Android 提供对应的 android 插件用于项目的构建配置,在 Android 中项目的类型有以下四种: AppExtension:对应 com.android.application。LibraryExtension:对应 com.android.library。TestExtension:对应 com.android.test。FeatureExtension:对应 com.android.feature,及时应用。

3.1 Android 插件脚本块配置

Android 构建系统编译应用资源和源代码,然后将它们打包成可供测试、部署、签署和分发的 APK。Android Studio 使用 Gradle 这一高级构建工具包来自动化执行和管理构建流程,同时也允许您定义灵活的自定义构建配置。每个构建配置均可自行定义一组代码和资源,同时对所有应用版本共有的部分加以重复利用。Android Plugin for Gradle 与这个构建工具包协作,共同提供专用于构建和测试 Android 应用的流程和可配置设置。

3.1.1 aaptOptions { }

配置 Android 资源打包工具 AAPT 选项。 aaptOptions { additionalParameters "-S", "/Users/yifan/dev/github/Testapp/app/src/main/res3", "-S", "/Users/yifan/dev/github/Testapp/app/src/main/res2", "--auto-add-overlay" noCompress "foo", "bar" ignoreAssetsPattern "!.svn:!.git:!.ds_store:!*.scc:.*:_*:!CVS:!thumbs.db:!picasa.ini:!*~" }这个选项用于配置 AAPT 资源打包时的一些处理,比如资源替换,这块内容可参照编译时替换资源。

3.1.2 adbOptions { }

配置 Android 调试工具 ADB 选项。通常我们通过 adb 指令来进行 Apk 的安装或卸载,或者一些文件拷贝工作,通过 adbOptions {} 脚本块同样可以在 Android Plugin 中进行配置,adbOptions {} 脚本块对应 AdbOptions 类,该类有两个属性: installOptions:adb 配置选项,是 List类型。timeOutInMs:是设置超时时间的,单位是毫秒,这个超时时间是执行adb这个命令的超时时间,int 类型。android { adbOptions { timeOutInMs 10 * 1000 installOptions "-r","-s" } }-l:锁定该应用程序-r:替换已存在的应用程序,也就是我们说的强制安装-t:允许测试包-s:把应用程序安装到 SD 卡上-d:允许进行降级安装,也就是安装的比手机上带的版本低-g:为该应用授予所有运行时的权限

3.1.3 buildTypes { }

当前项目的构建类型配置,对应的配置类型 BuildType 类,在 Android Studio 的中已经给我们内置了 release 和 debug 两种构建类型,这两种模式的主要差异在于能否在设备上调试以及签名不一样,这里会涉及到很多属性可以配置。 applicationIdSuffix:配置基于应用默认的 applicationId 的后缀,常用于构建变体应用。consumerProguardFiles:配置 .aar 文件中是否使用 Proguard 混淆。crunchPngs:针对 png 的优化,设置为 true 的时候会增加编译时间。debuggable:配置构建的 apk 是否能够进行 debug。javaCompileOptions:配置 Java 编译的配置jniDebuggable:配置构建类型的 apk native code 是否能够进行 debug。minifyEnabled:是否启用 Proguard 混淆。multiDexEnabled:是否使用分包。multiDexKeepFile:指定放到主 dex 中的文件。multiDexKeepProguard:配置指定的文件使用 Proguard。proguardFiles:混淆文件。shrinkResources:用于配置是否自动清理未使用的资源,默认为 false。signingConfig:签名文件。useProguardversionNameSuffix:类似于 applicationIdSuffix。zipAlignEnabled:zipAlign 优化 apk 文件的工具。buildTypes { release { minifyEnabled false crunchPngs true debuggable false shrinkResources true multiDexEnabled true multiDexKeepProguard file("proguard-rules.pro") // keep specific classes using proguard syntax multiDexKeepFile file("multiDexKeep.txt") minifyEnabled true proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" signingConfig zipAlignEnabled true applicationIdSuffix ".release" versionNameSuffix ".release" } debug { applicationIdSuffix ".debug" versionNameSuffix ".debug" } }

3.1.4 compileOptions { }

Java 编译选项,通常是针对 JDK 进行编码格式修改或者指定 JDK 的版本,对应的类是 CompileOptions。 compileOptions { encoding = "utf-8" sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 }

3.1.5 dataBinding { }

DataBinding 配置选项,查看源码可以发现对应 DataBindingOptions 类,该类包含四个属性: version:版本号。enabled:是否可用。addDefaultAdapters:是否使用默认的 Adapter。enabledForTests:是否用于 Test。dataBinding { enabled true version 1.0 }

3.1.6 defaultConfig { }

defaultConfig 也是 Android 插件中常见的一个配置块,负责默认的所有配置。同样它是一个 ProductFlavor,如果一个 ProductFlavor 没有被特殊配置,则默认使用 defaultFlavor 的配置,比如报名、版本号、版本名称等。常见的属性有: applicationId:applicationId 是 ProductFlavor 的一个属性,用于配置 App 生成的进程名,默认情况下是 null。minSdkVersion:指定 Apk 支持的最低 Android 操作系统版本。targetSdkVersion:用于配置 Apk 基于的 SDK 哪个版本进行开发。versionCode:同样是 ProductFlavor 的一个属性,配置 Apk 的内部版本号。versionName:配置版本名称。testApplicationId:配置测试 App 的报名,默认情况下是 applicationId + ".test"。testInstrumentationRunner:配置单元测试使用的 Runner,默认是 android.test.InstrumentationTestRunner,或者可以使用自定义的 Runner。signingConfig:配置默认的签名信息,对生成的 App 签名。proguardFile:用于配置使用的混淆文件。proguardFiles:配置混淆使用的文件,可以配置多个。defaultConfig { applicationId "com.andoter.dsw" minSdkVersion 15 targetSdkVersion 28 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" signingConfigs signingConfigs.release }

3.1.7 dexOptions { }

dex 的配置项,通常在开发的过程中,我们可以通过配置 dexOptions {} 提高编译速度,与之对应的是 DexOptions 接口,该接口由 DefaultDexOptions 默认实现,DefaultDexOptions 类中包含以下属性: preDexLibraries:默认 truejumboMode:默认 falsedexInProcess:默认为 true,所有的 dex 都在 process 中,提高效率javaMaxHeapSize:最大的堆大小maxProcessCount:最大的 process 个数threadCount:线程个数dexOptions { incremental true //是否增量,如果开启multi-dex, 此句无效 preDexLibraries true javaMaxHeapSize "4g" //java 编译的 Heap 大小 jumboMode true threadCount 8 //gradle输就输在了并行上, 都是串行, 增加线程数没用 // 设置最大的进程数:Memory = maxProcessCount * javaMaxHeapSize maxProcessCount 8 }

3.1.8 lintOptions { }

Lint 是Android Studio 提供的 代码扫描分析工具,它可以帮助我们发现代码结构/质量问题,同时提供一些解决方案,而且这个过程不需要我们手写测试用例。Lint 发现的每个问题都有描述信息和等级(和测试发现 bug 很相似),我们可以很方便地定位问题同时按照严重程度 进行解决。 android { lintOptions { // true--关闭lint报告的分析进度 quiet true // true--错误发生后停止gradle构建 abortOnError false // true--只报告error ignoreWarnings true // true--忽略有错误的文件的全/绝对路径(默认是true) //absolutePaths true // true--检查所有问题点,包含其他默认关闭项 checkAllWarnings true // true--所有warning当做error warningsAsErrors true // 关闭指定问题检查 disable "TypographyFractions","TypographyQuotes" // 打开指定问题检查 enable "RtlHardcoded","RtlCompat", "RtlEnabled" // 仅检查指定问题 check "NewApi", "InlinedApi" // true--error输出文件不包含源码行号 noLines true // true--显示错误的所有发生位置,不截取 showAll true // 回退lint设置(默认规则) lintConfig file("default-lint.xml") // true--生成txt格式报告(默认false) textReport true // 重定向输出;可以是文件或"stdout" textOutput "stdout" // true--生成XML格式报告 xmlReport false // 指定xml报告文档(默认lint-results.xml) xmlOutput file("lint-report.xml") // true--生成HTML报告(带问题解释,源码位置,等) htmlReport true // html报告可选路径(构建器默认是lint-results.html ) htmlOutput file("lint-report.html") // true--所有正式版构建执行规则生成崩溃的lint检查,如果有崩溃问题将停止构建 checkReleaseBuilds true // 在发布版本编译时检查(即使不包含lint目标),指定问题的规则生成崩溃 fatal "NewApi", "InlineApi" // 指定问题的规则生成错误 error "Wakelock", "TextViewEdits" // 指定问题的规则生成警告 warning "ResourceAsColor" // 忽略指定问题的规则(同关闭检查) ignore "TypographyQuotes" } }

3.1.9 packagingOptions { }

Android 打包配置项,可以配置打包的时候哪些打包进 Apk。当项目中依赖的第三方库越来越多时,有可能会出现两个依赖库中存在同一个 (名称)文件。如果这样,Gradle在打包时就会提示错误(警告)。那么就可以根据提示,然后使用以下方法将重复的文件剔除。 packagingOptions { //这个是在同时使用butterknife、dagger2做的一个处理。同理,遇到类似的问题,只要根据gradle的提示,做类似处理即可。 exclude "META-INF/services/javax.annotation.processing.Processor" }

3.1.10 productFlavors { }

用于构建不同的产品风味,在上面我们提到 defaultConfig{} 也是一种产品风味,可作为所有产品风味的“基类”共同部分。风味(Flavor) 对应 ProductFlavor 类,该类的属性与配置属性相匹配。 android { ... defaultConfig {...} buildTypes {...} productFlavors { demo { applicationIdSuffix ".demo" versionNameSuffix "-demo" } full { applicationIdSuffix ".full" versionNameSuffix "-full" } } }在创建和配置您的产品风味之后,在通知栏中点击 Sync Now。同步完成后,Gradle会根据您的构建类型和产品风味自动创建构建变体, 并按照的格式命名这些变体。例如,如果您创建了“演示”和“完整”这两种产品风味并保留默认的“调试”和“发布”构建类型,Gradle 将创建以下构建变体: 演示调试演示发布完整调试完整发布您可以将构建变体更改为您要构建并运行的任何变体,只需转到 Build >Select Build Variant,然后从下拉菜单中选择一个变体。产品风味组合通常在适配多个渠道的时候,需要为特定的渠道做部分特殊的处理,这里就会涉及到结合 buildTypes{} 组合不同的产品风味组合。 android { ... buildTypes { debug {...} release {...} } // Specifies the flavor dimensions you want to use. The order in which you // list each dimension determines its priority, from highest to lowest, // when Gradle merges variant sources and configurations. You must assign // each product flavor you configure to one of the flavor dimensions. flavorDimensions "api", "mode" productFlavors { demo { // Assigns this product flavor to the "mode" flavor dimension. dimension "mode" ... } full { dimension "mode" ... } // Configurations in the "api" product flavors override those in "mode" // flavors and the defaultConfig {} block. Gradle determines the priority // between flavor dimensions based on the order in which they appear next // to the flavorDimensions property above--the first dimension has a higher // priority than the second, and so on. minApi24 { dimension "api" minSdkVersion "24" // To ensure the target device receives the version of the app with // the highest compatible API level, assign version codes in increasing // value with API level. To learn more about assigning version codes to // support app updates and uploading to Google Play, read Multiple APK Support versionCode 30000 + android.defaultConfig.versionCode versionNameSuffix "-minApi24" ... } minApi23 { dimension "api" minSdkVersion "23" versionCode 20000 + android.defaultConfig.versionCode versionNameSuffix "-minApi23" ... } minApi21 { dimension "api" minSdkVersion "21" versionCode 10000 + android.defaultConfig.versionCode versionNameSuffix "-minApi21" ... } } }Gradle 创建的构建变体数量等于每个风味维度中的风味数量与您配置的构建类型数量的乘积。在 Gradle 为每个构建变体或对应 APK 命名时,属于较高优先级风味维度的产品风味首先显示,之后是较低优先级维度的产品风味,再之后是构建类型。以上面的构建配置为例,Gradle 可以使用以下命名方案创建总共 12 个构建变体: 构建变体:[minApi24, minApi23, minApi21][Demo, Full][Debug, Release] 对应 APK:app-[minApi24, minApi23, minApi21]-[demo, full]-[debug, release].apk同样我们在 Terminal 中通过 gradle 指令执行构建变体的任务:变体名称(flavorDimensions 的第一个维度的 Flavor) + flavorDimensions 的别的维度的 Flavor + buildTypes,例如:minApi24DemoDebug,构建出来的对应 Apk:app-minApi24-demo-debug.apk过滤变体Gradle 会自动根据设置的 buildTypes{} 和 flavorDimensions{} 创建很多变体的组合体,但是有些是我们不需要的,这里就涉及到过滤变体的操作。Gradle 提供了 variantFilter {} 脚本块来过滤,脚本块对应 VariantFilter 接口,接口中包含: setIgnore(boolean ignore):设置是否忽略某个变体getBuildType:获取变体的构建类型ListgetFlavors():返回所有变体的列表getName:获取变体的 name 名称variantFilter { variant ->def names = variant.flavors.name // To check for a certain build type, use variant.buildType.name == "" if (names.contains("minApi23") && names.contains("demo")) { // Gradle ignores any variants that satisfy the conditions above. setIgnore(true) } else { setIgnore(false) } }更多关于构建变体内容参照官方:构建变体

3.1.11 signingConfigs { }

配置签名信息,常用于 BuildType 和 ProductFlavor 配置,在构建变体的过程中,会出现很多种类,所以针对不同类别的变体所使用的签名可能也是不同的,这就需要使用 signingConfigs{} 配置签名信息合集,然后按需所取。 signingConfigs { release {//发布版本的签名配置 storeFile file(props["KEYSTORE_FILE"]) keyAlias props["KEY_ALIAS"] storePassword props["KEYSTORE_PWD"] keyPassword props["KEY_PWD"] } debug {//调试版本的签名配置 storeFile file(props["DEBUG_KEYSTORE"]) keyAlias props["DEBUG_ALIAS"] storePassword props["DEBUG_KEYSTORE_PWD"] keyPassword props["DEBUG_KEY_PWD"] } } buildTypes { signingConfig signingConfigs.release }

3.1.12 sourceSets { }

在 AndroidStudio 中,在 src/main/java 目录下创建我们的 .java 文件,这些都是系统通过 sourceSet{} 设置好的,比如我们在外部创建一个文件夹,选中文件夹右键就无创建 .java 文件的选项。就需要我们通过 sourceSets 进行配置,脚本块对应 AndroidSourceSet 接口,接口中有: AndroidSourceSet java(Closure configureClosure):配置 java 文件存放路径AndroidSourceSet resources(Closure configureClosure):配置 resource 目录AndroidSourceSet jniLibs(Closure configureClosure):配置 jniLibs 目录AndroidSourceSet jni(Closure configureClosure):配置 jni 文件目录AndroidSourceSet renderscript(Closure configureClosure):配置 renderscript 目录AndroidSourceSet aidl(Closure configureClosure):配置 aidl 文件目录AndroidSourceSet assets(Closure configureClosure):配置 assets 目录AndroidSourceSet res(Closure configureClosure):配置 res 目录AndroidSourceSet manifest(Closure configureClosure):配置 manifest 目录sourceSets { main { java { srcDir "src/main/testjava" } resources { srcDir "src/resources" } } }

3.1.13 splits { }

拆分机制比起使用 flavors,能让应用程序更有效地构建一些形式的多个apk。 多 apk 只支持以下类型: 屏幕密度ABI脚本块对应 Splits 类,该类中有三个属性: density:像素密度abi:abi 类型language:语言splits { density { enable true exclude "ldpi", "tvdpi", "xxxhdpi" compatibleScreens "small", "normal", "large", "xlarge" } abi { enable true reset() include "x86", "armeabi-v7a", "mips" universalApk true }

用户评论

安陌醉生

终于能开始学 Android 开发了!

    有18位网友表示赞同!

ゞ香草可樂ゞ草莓布丁

之前一直对 Gradle 不了解,打算好好学习一下。

    有6位网友表示赞同!

孤者何惧

这个入门指南看起来很不错,希望能够轻松学会。

    有6位网友表示赞同!

算了吧

从零开始学习安卓开发太棒啦!

    有7位网友表示赞同!

爱你心口难开

我准备跟着这篇教程来入门 Android 开发。

    有13位网友表示赞同!

作业是老师的私生子

对 Gradle 的语法一直有些迷茫,希望能通过这个指南解决困惑。

    有10位网友表示赞同!

凝残月

终于找到一篇适合初学者的 Android Gradle 教程了!

    有13位网友表示赞同!

单身i

学习安卓开发需要掌握好 Gradle 嘛?

    有6位网友表示赞同!

无望的后半生

想要开发自己的安卓APP,Gradle 必不可少吧?

    有17位网友表示赞同!

羁绊你

看了这篇标题,感觉马上就可以开始写代码了!

    有10位网友表示赞同!

身影

这个指南太棒了,有图文并茂的讲解吗?

    有15位网友表示赞同!

青衫故人

希望能清楚地了解 Gradle 的基本概念和用法。

    有20位网友表示赞同!

拽年很骚

Android 开发好入门,Gradle 是基础知识啊!

    有10位网友表示赞同!

?亡梦爱人

期待这篇指南能够给我带来一些新的收获。

    有15位网友表示赞同!

哥帅但不是蟋蟀

学习 Android 开发一直是我心中的目标,现在终于开始了!

    有5位网友表示赞同!

?娘子汉

希望这个入门指南能把我带领进入安卓开发的世界。

    有12位网友表示赞同!

淡抹丶悲伤

Android Gradle 是挺重要的工具啊,得好好学习一下!

    有17位网友表示赞同!

秒淘你心窝

感觉这篇教程很专业,应该可以信赖它的内容。

    有20位网友表示赞同!

何年何念

希望能够掌握 Android Gradle 之后,我能开发出很棒的APP!

    有6位网友表示赞同!

【Android 构建工具基础教程:Gradle 初学者指南】相关文章:

1.蛤蟆讨媳妇【哈尼族民间故事】

2.米颠拜石

3.王羲之临池学书

4.清代敢于创新的“浓墨宰相”——刘墉

5.“巧取豪夺”的由来--米芾逸事

6.荒唐洁癖 惜砚如身(米芾逸事)

7.拜石为兄--米芾逸事

8.郑板桥轶事十则

9.王献之被公主抢亲后的悲惨人生

10.史上真实张三丰:在棺材中竟神奇复活

上一篇:DIY教学:人体血液体循环探索手册 下一篇:福建莆田鞋批发:10-20元品质鞋款,进货价20元