Android Gradle 基础入门

android studio 下载 | 2018-10-26 05:46

换新工作后很少更新公众号了,主要是后面考虑下代码放在手机上查看毕竟达不到效果,所以后续都会把自己的总结形成 PDF 文档。公众号还是比较适合纯文字的总结类。

由于现在 CSDN 私自会把上传的资源更改积分下载,所以本文的 PDF 文档托管在 Github 上,地址: -summary/blob/master/work-blog/pdf/Android%20Gradle%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8.pdf

在 Android Studio 构建的项目中,基于 Gradle 进行项目的构建,同时使用 Android DSL 进行 Android 项目的配置,而 Gradle 是基于 Groovy 脚本语言进行开发,所以熟练掌握 Android Studio 中的项目配置,就需要掌握以下知识点:

Groovy 基础

Gradle DSL 语法

Android DSL 语法

Groovy 和 Java 一样也是一门 JVM 语言,最终都会编译成 .class 文件然后运行在 JVM 上,Groovy 语言类似脚本,语法简单更灵活,所以在编写项目脚本构建上优势更加明显。

工欲善其事必先利其器,编码之前环境先行。网上有很多配置 Groovy 开发的环境,针对 Android 程序员来说,使用我们的 Android Studio 就能满足。

第一步:创建一个 Application 或 Library 工程

工程创建好后,除了 build.gradle 文件和 src/main 目录文件夹保留外,其它的都可以删除。

第二步:在 src/main 目录下创建一个 groovy 文件夹

在创建的 Module 工程中的 build.gradle 文件中添加以下依赖:

apply plugin: 'groovy'dependencies {    compile gradleApi()    compile localGroovy()}repositories {    mavenCentral()}第三步:在 groovy 文件夹中创建 .groovy 文件

Android Studio 中会自动识别 groovy 文件夹下的 .groovy 文件。

第四步:配置运行环境

Groovy 是弱化类型语言,但是实现上还是强类型相关,如果类型不对,还是会报错。Groovy 中的基本数据类型有:

byte -这是用来表示字节值。例如2。

short -这是用来表示一个短整型。例如10。

int -这是用来表示整数。例如1234。

long -这是用来表示一个长整型。例如。

float -这是用来表示32位浮点数。例如12.34。

double -这是用来表示64位浮点数,这些数字是有时可能需要的更长的十进制数表示。例如12.。

char -这定义了单个字符文字。例如“A”。

Boolean -这表示一个布尔值,可以是true或false。

String -这些是以字符串的形式表示的文本。例如,“Hello World”的。

int a = 4;float b = 1.0;double c = 2.1;byte  te = 4;char ch = 'c';String str = "abcd";1.2.2 变量和方法的声明在 Groovy 中通过 def 关键字进行变量和方法的声明。

def a = 1def b = 1.0def str = "Hello World"def output() {    print 'Hello World'}Groovy 类似脚本,所以有很多都可以省略:

语句后的分号;可以省略

变量的类型可以省略

方法返回值 return 语句可以省略

方法的声明

Groovy 也是一门 JVM 语言,所以在语法上与 Java 有很多相通的地方,这样在方法的声明时候格式也比较随意,所以作为 Android 程序员,我们可以选择靠拢 Java 语法格式的风格。

def methodName() {    print("HelloWorld")}def int sum2Number(a, b) {    return a + b}def sum(a, b) {    return a+b}int add(a ,b) {    return a + b}1.2.3 循环Groovy 中循环控制语句与 Java 中的类似,有以下三种:

while 语句

for-in 语句

for(int i = 0 ;i < 9; i++) {    println(i)}int i = 0while(i++ < 9) {    println(i)}for(int j in 1..9) {    println(j)}同样,针对循环也有循环控制语句:

break:break语句用于结束循环和switch语句内的控制流。

continue:结束本次循环,进行下次循环,仅限于while和for循环。

for(int i = 0 ;i < 9; i++) {    println(i)    continue}for(int j in 1..9) {    println(j)    break}1.2.4 条件判断语句Groovy 中的条件判断语句与 Java 中的类似,有:

if…else if…else

例子就不演示了,语法跟 Java 相同。

在上面的 Groovy 基础介绍中,形式上跟 Java 语言非常相似,没有太大的变化,针对 Java 、Android 程序员来说应该非常容易上手。

Java 中集合主要有:List、Map 衍生出来的类,在 Groovy 中同样存在集合,名称跟 Java 中的相同,只不过形式发生改变了,更加简单容易操作。Groovy 中的集合:

List:亦称为列表,列表是用于存储数据项集合的结构。

Map:亦称为映射,映射(也称为关联数组,字典,表和散列)是对象引用的无序集合。

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

def listEmpty = []    //空列表def list = [1,2,3,4,5] //整数值列表def listInt = [1,[2,3],4,5] //列表嵌套列表def listString = ["andoter","note"] //字符串列表def listNone = ["andoter",1,4]  //异构对象列表`

列表中的方法:

boolean add(E e)

void add(int index, E element)

boolean addAll(Collection c)

void clear()

boolean contains(Object o)

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] //列表嵌套列表def listString = ["andoter","note"] //字符串列表def listNone = ["andoter",1,4]  //异构对象列表listEmpty.add(1)listEmpty << 6println(listEmpty.size())list.clear()println(listInt.contains([2,3]))println(listString.lastIndexOf("note"))println(listNone.indexOf(1))需要注意,在 groovyjarjarantlr.collections.List 包下同样存在 List,所以使用的时候需要注意。

关于列表 List 的遍历,我们可以参照 Java 中的 Iterator 接口去遍历,或者使用 Groovy 系统提供的 each 方法进行遍历。在 Groovy 中提供 DefaultGroovyMethods 类,该类定义很多快捷使用方法:

abs:取绝对值计算

addAll(Collection)

each:遍历

eachWithIndex:带 index 的遍历

grep:符合条件的element会被提取出来,形成一个list

every:所有的element都满足条件才返回true,否则返回false

any:只要存在一个满足条件的element就返回true,否则返回false

join:用指定的字符连接collection中的element

sort:根据指定条件进行排序

find:查找collection中满足条件的‘第一个’element

findAll:查找collection中满足条件的‘所有’element

很多使用的方法,可参照源码查看。

def listString = ["andoter","note"]listString.each {    println(it)}listString.each {    value -> println(value)}1.3.2 Map 映射Map集合中的元素由键值访问。 Map中使用的键可以是任何类。当我们插入到Map集合中时,需要两个值:键和值。

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

void clear()

boolean containsValue(Object value)

<map.entry

> entrySet()</map.entry

void forEach(BiConsumer action)

总体上方法与 Java 中的 Map 相同。

def mapEmpty = [ : ]def mapString = ["name":"andoter", "email" : ""]def mapInt = ["name" : 123, "age" : 26]mapEmpty.put("name","andoter")mapEmpty.values()mapString.get("name")mapInt.containsValue("123")mapString.each {key, value ->    if(key == null || key.length() == 0) {        println("Null Object")    }    if(key.equals("name")){        println(key + "=" + value)    }else{        println(key + ":" + value)    }}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 (file.exists()) {   file.eachLine {       line ->           println(line)   }} else {    print("File not exist")}这里简单的示例下,更多的内容请参照官方 API 接口。

闭包作为 Groovy 中非常重要的特性,它使得 Groovy 语言更加灵活,在 Gradle 项目构建中,更是在 DSL 中大量被使用,所以掌握闭包的使用对掌握 Android 项目构建有非常重要的作用。

闭包的定义格式:

{    parameters -> statements}从形式上来看与 Lambda 表达式非常类似,所以熟悉 Lambda 表达式的同学上手闭包非常简单。如果闭包没有定义参数,它隐含一个参数 it,类似 Java 中的 this,假设你的闭包不需要接受参数,但是还是会生成一个隐式参数 it,只不过它的值为 null,也就是说,闭包至少包含一个参数。

无参数的闭包

def closure = {    println("No Parameters")}一个参数的闭包

def closureOneParameters = {    key -> println(key)}两个参数的闭包

def closure2Parameter = {    key,value->        if (key == 1) {            key = key + 1            println(key + ":" + value)        } else if (key == 2)            println(key + ":" + value)}1.5.2 闭包的特性闭包的引入让 Groovy 语言更加简单、方便,比如作为函数的最后一个参数,闭包可以单独写在函数,本小节中介绍一下闭包常见的使用形式。

闭包特性:

闭包可以访问外部的变量,方法是不能访问外部变量的。

闭包中可以包含代码逻辑,闭包中最后一行语句,表示该闭包的返回值,不论该语句是否冠名return关键字,如果最后一行语句没有不输入任何类型,闭包将返回null。

闭包的参数声明写在‘->’符号前,调用闭包的的标准写法是:闭包名.call(闭包参数)。

闭包的一些快捷写法,当闭包作为闭包或方法的最后一个参数。可以将闭包从参数圆括号中提取出来接在最后,如果闭包是唯一的一个参数,则闭包或方法参数所在的圆括号也可以省略。对于有多个闭包参数的,只要是在参数声明最后的,均可以按上述方式省略。

闭包作为函数参数

闭包作为函数参数时,跟普通的变量参数使用方式相同。

def checkKey = {    map ->        if (map.size() == 0) {            println("Parametes is Null or Empty")        }        println(map)}def enqueue(key, value, closure) {    def map = [:]    map.put(key, value)    closure(map)}enqueue(1, 2, checkKey)通常情况下,在函数具有闭包作为参数的时候,会将闭包放在最后一个参数的位置,当闭包作为最后一个参数的时候,闭包可以抽离到函数体之外,提高函数的简洁性。

关于 Groovy 比较好的文章

深入理解Android之Gradle

Groovy进阶之函数、闭包和类

上面我们针对 Groovy 语言进行简单的学习,接下来就是 Gradle DSL 语言的学习。Gradle 是 Android Studio 中采用的全新项目构建方式。

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

Gradle 是一种脚本配置,所以当它执行的时候,它需要跟对应的类型相对应。在 Gradle 中存在以下三种类型:

Gradle 围绕项目 Project,所以 Project 是我们最重要的接口,通过 Project 接口,我们可以获取整个 Gradle 的属性。通常我们的项目在 Project 模式的下结构是:

├── app #Android App目录│   ├── app.iml│   ├── build #构建输出目录│   ├── build.gradle #构建脚本│   ├── libs #so相关库│   ├── proguard-rules.pro #proguard混淆配置│   └── src #源代码,资源等├── build│   └── intermediates├── build.gradle #工程构建文件├── gradle│   └── wrapper├── gradle.properties #gradle的配置├── gradlew #gradle wrapper linux shell脚本├── gradlew.bat├── LibSqlite.iml├── local.properties #配置Androod SDK位置文件└── settings.gradle #工程配置2.2 Project2.2.1 生命周期 LifecycleProject 与 build.gradle 文件是一对一的关系,在初始化脚本构建的过程中,Gradle 为每一个项目创建 Project对象。步骤如下:

初始化阶段

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

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

include ':app', ':groovydemo'指明了需要编译的项目。

配置阶段

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

执行阶段

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

Task 是 Gradle 中的最小执行单元,所有的构建、编译、打包、debug、test 等都是执行了某一个 task,一个 Project 可以有多个 Task,Task 之间可以互相依赖。例如我有两个 Task,TaskA 和 TaskB,指定 TaskA 依赖 TaskB,然后执行 TaskA,这时会先去执行 TaskB,TaskB 执行完毕后在执行 TaskA。

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

String getName():获取任务名称

Project getProject():获取任务所在的 Project 对象

<action</action

TaskDependency getTaskDependencies():获取任务依赖

Task dependsOn(Object… var1):任务依赖关系函数

void onlyIf(Closure var1)

TaskState getState():获取任务的状态

Task doFirst(Closure var1):任务先执行..

Task doLast(Closure var1):任务后执行..

String getDescription():获取任务描述

String getGroup():获取任务分组

Task 是 Gradle 中最小执行基本单元,创建任务的方式有以下几种:

Project.task(String name)

Project.task(String name, Closure configureClosure)

Project.task(Map

Project.task(Map

TaskContainer.create(String name)

TaskContainer.create(Map

前面三种都是基于 Project 提供的 task 重载方法进行创建。这里需要着重介绍下里面的 Map 参数,Map 参数选项用于控制 Task 的创建以及属性。

第一种:直接以任务名称创建任务

Task copyTask = task("copyTask")copyTask.description = "Copy Task"copyTask.group = "custom"copyTask.doLast {    print("Copy Task Create")}这种方式跟 Java 中的创建对象的方式非常相似,这种方式的本质是调用 Project 类中的 task(String name) 方法进行对象的创建。

第二种:task + 闭包的方式

Task taskClosure = project.task("taskClosure"){    print("Task Closure")}这种写法利用闭包是最后一个参数的时候,可以抽取到外部写。上面的这种写法也可以精简:

task taskClousre {    print("Task Closure")}这种形式的在 .gradle 脚本文件中用的非常多,所以大家也写这种吧!

第三种:task + Map

Task mapTask = project.task(dependsOn: copyTask,description: "mapTask",group: "mapTask",        "mapTask"){    println("Map Task Create")}这里通过 Map 进行 Task 的一些设置,这里我们可以同样以方式一一样,单独进行设置。

第四种:TaskContainer 创建任务

project.tasks.create("TaskContainer") {    description "TaskContainer"    group "TaskContainer"    doLast {        println("TaskContainer")    }}在上面的演示例子中,我们也介绍了任务的分组和描述的使用,可以在 Gradle Pojects 栏中进行查看任务的分组和描述。

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 类型 Task

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

配置整个 Project 和子项目的配置。

allprojects {    repositories {        google()        jcenter()    }}比如我们在 allprojects 内部定义的 Task 任务,就会用于根目录和子项目。比如下面的例子,我们执行:./gradlew print 任务,打印的结果如下:

allprojects {    task print {        println project.name    }}输出结果:GradlePlugin(根目录)appgroovydemosensorsdatalibrary2.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 dependenciesdependencies {  //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 ""        //如果上面的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    }}输出结果:appgroovydemosensorsdatalibrary2.3.8 publishing { }用于发布构建。

publishing {  publications {    myPublication(MavenPublication) {      from components.java      artifact sourceJar      pom {        name = "Demo"        description = "A demonstration of Maven POM customization"        url = ""        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 = ""          }        }        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 配置文件

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

apply plugin: plugin idapply plugin : 'java'2.4.2 脚本插件通常脚本插件用于本地的配置存储,使用格式:

apply from: ‘fileName’// config.gradlerootProject.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<Project> {@Overridevoid 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。groovy<br />implementation-class=com.andoter.customplugin.MyPlugin<br />

打开 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'    }}//格式为--&gt;group:module:versiondependencies {    classpath 'com.android.tools.build:gradle:3.1.2'    classpath 'com.andoter.customplugin:groovydemo:1.0'}}// 子项目使用插件apply plugin: 'com.andoter.customplugin'这样就完成一个自定义插件的使用步骤,自定义插件的核心开发一个什么样的插件,比如结合 Transform 开发一个编译时框架。

Gradle 的功能非常强大,这里也仅仅针对 Android 项目构建常用的进行总结,Gradle 还可用于单元测试,或者用于别的项目构建,上传构建很多部分。具体可以参照官方 API 文档。

Gradle Guides 官方文档

Android DSL 官方文档

Gradle学习笔记

迁移到 Android Plugin for Gradle 3.0.0

一个不甘平凡的码农