Gradle学习总结

96
作者 zyq_neuq
2016.12.22 13:11 字数 3235

本篇主要是个人学习gradle的笔记总结

一.开始之前

1. 为什么学习Gradle

  • 采用DSL(Domain Specific Language)语言来描述和控制构建逻辑。(所谓领域专用语言,其基本思想是“求专不求全”,不像通用目的语言那样目标范围涵盖一切软件问题,而是专门针对某一特定问题的计算机语言。)
  • 支持Maven或者Ivy的依赖管理。
  • 插件可以提供自己的DSL和API供文件使用。
  • 让代码或资源复用更容易。
  • 让创建同一应用程序的不同版本变得更加容易,无论是多个 apk 发布版本还是同一个应用的不同定制版本。

2. 学习条件

由于Android最新推荐的编译方式是采用Gradle进行编译,因此我们必须学习Gradle的基本知识, 而Gradle是基于Groovy语法编写的,因此我们必须了解Groovy语法 ,而Groovy又是基于Java语言的,是java语言的扩展, 所以说只要我们会java语言就可以编写出Gradle脚本。

3. 学习目标

A: 学习Groovy基本概念, 学习Groovy集合, 类,和闭包语法。
B: 学习Gradle 基本用法, 能够编写task,理解project概念。
C: 可以理解android的编译配置。

二.Groovy初探

1. 什么是Groovy

Grovvy是JVM的一个替代语言, 是指可以用Groovy在Java平台上进行Java编程,使用方式基本和Java代码先同,它也是Java的扩展。

2. 开发环境配置

http://www.groovy-lang.org/learn.html
配置方式如下:


grovvy1.png

3. Groovy的特性

Groovy松散的java语法允许省略分号和修饰符;除非另指定,groovy所有内容都为public;允许定义简单脚本,无需定义正规的class对象;允许省略变量类型。

4. Groovy和java的对比

http://www.groovy-lang.org/differences.html

如下:读取文件

Path file = Paths.get("/path/to/file");
Charset charset = Charset.forName("UTF-8");
try (BufferedReader reader = Files.newBufferedReader(file, charset)) {      
    String line;
    while ((line = reader.readLine()) != null) { 
       System.out.println(line); 
    }
} catch (IOException e) { 
    e.printStackTrace();
}

can be written like this:

new File('/path/to/file').eachLine('UTF-8') { 
  println it
}

or, if you want a version closer to Java:

new File('/path/to/file').withReader('UTF-8') { 
  reader -> reader.eachLine { 
    println it 
  }
}

5. Groovy集合,闭包,映射

  1. 范围 :for (i in 0..4) 将包含的范围限制在0-4间。

  2. 集合。def coll = ["Groovy", "Java", "Ruby"] coll << “smalltalk”

  3. 映射 def hash = [“name”:”andy”, “vip”:”45”]

  4. 闭包 coll.each{ println it} 或 coll.each{value -> println value}

如果闭包没有定义参数,那么隐含一个参数it, 和this作用类似。

对于闭包的参数需要查看api文档。

6. 类

Groovy类就是java类,初始化时Groovy 自动提供一个构造函数,构造函数接受一个名称-值对的映射,这些名称-值对与类的属性相对应。这是 Groovy 的一项开箱即用的功能 — 用于类中定义的任何属性,Groovy 允许将存储了大量值的映射传给构造函数。还会对成员变量定义get,set方法,所以可以直接点引用。

7. 看具体例子。

Demo地址:

Groovy API文档为: http://www.groovy-lang.org/api.html

三.Groovy深入

1. Groovy和JVM的关系

除了语言和Java相通外,Groovy有时候又像一种脚本语言。当我执行Groovy脚本时,Groovy会先将其编译成Java类字节码,然后通过Jvm来执行这个Java类。


grovvy2.png

实际上,由于Groovy Code在真正执行的时候已经变成了Java字节码,所以JVM根本不知道自己运行的是Groovy代码.

2. 脚本和类

既然是基于java来执行的,那我们将groovy转会为java类。

执行 groovyc-d classes test.groovy

groovyc是groovy的编译命令,-d classes用于将编译得到的class文件拷贝到classes文件夹下。

我们可以看到helloworld.groovy 被转化为java class类,继承自Script。

Song 类被转化为实现GroovyObject的类,并封装了get,set方法。

可以看出, groovy既可以作为类来使用, 又可以作为脚本来使用。

Groovy 脚本的代码其实都会被放到run函数中执行的。变量作用域,区分def和不用def定义, def定义的为run函数作用域,无def的为全局属性,细节可以看字节码。

3. IO操作

def targetFile = new File(xxx)

targetFile.eachLine{ line ->

    println line

}

直接读取文件: targetFile.getBytes()

获取输入流: def is = targetFile.newInputStream() is.close

闭包输入流:

targetFile.withInputStream{ is ->

   操作is, 无需关闭输入流, 闭包会自动关闭。

}

写文件:

def srcFile = new File(源文件)

def targetFile = new File(目标文件)

targetFile.withOutputStream{os ->

srcFile.withInputStream{ is ->

    os << is

}

}

这里重载了<< 操作符, 细节可以查看api文档,方法名为leftShift

四.Gradle初探

1. 什么是Gradle

用户手册:https://docs.gradle.org/current/userguide/userguide.html

API文档:https://docs.gradle.org/current/javadoc/

DSL手册:https://docs.gradle.org/current/dsl/

Gradle 是配置编译脚本, 也是编程开发框架。

Gradle 由一个或多个proejct组成, 每一个待编译的工程都叫一个project,每一个project在构建的时候都包含一系列的task。比如一个Android apk的编译可能包含:java源码编译Task,资源编译Task,JNI编译Task,lint检查Task, 打包生成APK的task,签名Task等等。一个project到底包含多少Task,其实是由编译脚本指定的插件决定。插件就是用来定义Task,并且具体执行这些task的东西。

2. 开发环境配置

http://gradle.org/ 下载对应的gradle文件到本地,配置环境变量即可。

GRADLE_HOME=/Users/zhangyuqiang/work/soft/gradle-2.2.1;

加到~/.bash_profile 中, export GRADLE_HOME即可.

下面Demo地址:https://github.com/davenkin/gradle-learning

3. Task

Task是gradle中的一种数据类型,它代表了一些药执行或者要干的工作。每一个task都要和一个project关联。

创建一个task

task hello {

 doLast{

    println “hello gradle”

 }

}

或者

task hello << {

   println “hello gradle”

}

参考Demo: 2-define-task, 3-undertand-gradle-syntax,

4. Project

每一个build.gradle 就是一个project。一个project中会有一个或多个task。

5. 属性

(Demo: 5-define-custom-properties)

gradle 的project中会有一些默认的属性,可以理解为类的成员变量,我们还可以给project增加一些额外属性, 通过ext。

6. 依赖关系

(Demo: 7-dependency-management, 8-multi-project)
project依赖可以通过dependencies{}来设置依赖,task依赖可以通过dependsOn 来设置。

7. Multi-Project组织

(Demo: 8-multi-project)

多project管理,主要依靠settings.gradle ,在该文件中include 需要编译的模块名即可。可以在该文件中增加函数,如initGradleEnvironment(),这些函数会在gradle构建整个工程任务的时候执行。其实include也是函数。

8. Gradle命令

gradle projects

gradle tasks

gradle taskname 执行任务

gradle clean, assembleDebug, aR, build …

五.Gradle深入

1. Gradle工作流程


gradle1.png

首先是初始阶段,对于multi-project而言,就是settings.gradle配置执行。

其次是配置阶段,该阶段解析每个project中的build.gradle。这两个阶段我们可以增加hook进来,执行相关任务。

最后是执行阶段,执行完成后我们也可以增加hook。

Gradle基于groovy,所以编译执行时gradle也会把脚本转化为java对象。

Gradle中主要有三种对象, 这三种对象和三种不同的脚本文件对应, 在Gradle执行的时候,会将脚本转化成对应的对象,分别为Gradle对象,Project对象,Settings对象。

2. Gradle编程模型及生命周期

https://docs.gradle.org/current/userguide/build_lifecycle.html

3. Gradle对象

当我们执行gradle xxx时,Gradle会从默认的配置脚本中构造出一个Gradle对象, 在整个执行过程中只有这么一个对象, Gradle对象的数据类型就是Gradle。我们一般很少去定制这个脚本

4. Settings对象

settings.gradle 会被转化为一个Settings对象。

5. Project对象

每个build.gradle 都会转化为一个Project对象。由于project对应到具体工程, 因此要为project加载对应的插件。其实每一个project具体包含多少个task是由插件决定的。

6. 代理机制

Gradle大量地使用了Groovy闭包的delegate机制。简单来说,delegate机制可以使我们将一个闭包中的执行代码的作用对象设置成任意其他对象。3-undertand-gradle-syntax。

apply方法, 使用: apply plugin: ‘com.android.library’, 如果编译lib,则使用此插件, 也可以加载一个gradle文件,如: apply from : ‘utils.gradle’。

此处可以utils.gradle定义的属性和方法。为什么可以使用呢? 我们知道gradle和groovy一样,每个脚本都继承自Script。utils.gradle 也会被转化为一个Script对象, Script对象中有一个delegate对象, 在apply函数中有个from参数,还有个to参数, 通过to参数可以将delegate对象指向别的东西,这里即utils 的Scrpit类, 当你在自己的Script中操作一些不是自己定义的变量或者函数时,gradle会到Script的delegate对象中去找。

7. BuildScriptBlock


gradle2.png

六.Android中gradle基本配置

https://developer.android.com/intl/zh-cn/tools/building/plugin-for-gradle.html

http://google.github.io/android-gradle-dsl/current/

1. 插件


gradle3.png

2. 各Script配置

android中的BuildScriptBlock


gradle4.png

3. gradle命令及依赖配置

依赖配置有:模块依赖,本地依赖,远程依赖。


gradle5.png

命令:顶级命令有4个

  assemble  Builds the project output.

  Check  Runs checks and tests.

  Build   Runs both assemble and check.

  Clean  Performs the clean

常用的有gradle assembleDebug 编译debug包。

Gradle assembleRelease, 编译release包, 缩写为gradle aR.

4. multi-dex 配置

https://developer.android.com/intl/zh-cn/tools/building/multidex.html

七.Android中gradle的高级配置

1. 构建变种版本-BuidVariant。

构建类型+定制产品=构建变种版本

BuildType + ProductFlavor 任何一种组合都会是一个版本。

http://wiki.jikexueyuan.com/project/android-gradle-guide/build-variants.html

BuildType : 可以分为debug, release, publish , product, demo 等等。

ProductFlavor:可自定义为flavor1,flavor2…

android {
        ...

        defaultConfig {
            minSdkVersion 8
            versionCode 10
        }

        productFlavors {
            flavor1 {
                packageName "com.example.flavor1"
                versionCode 20
            }

            flavor2 {
                packageName "com.example.flavor2"
                minSdkVersion 14
            }
        }
    }
每一个Variant也会创建额外的sourceSet:
android.sourceSets.flavor1Debug
 位于src/flavor1Debug/
android.sourceSets.flavor1Release
 位于src/flavor1Release/
android.sourceSets.flavor2Debug
 位于src/flavor2Debug/
android.sourceSets.flavor2Release
 位于src/flavor2Release/

这些sourceSet拥有比Build Type的sourceSet更高的优先级,并允许在Variant的层次上做一些定制。

2. 高级构建选项

  android {
        aaptOptions {
            noCompress 'foo', 'bar'
            ignoreAssetsPattern "!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~"
        }
    }

aapt操作配置将应用到所有task上。

3. 操作Task

Android项目中会有大量的task, 并且他们基于buildType和productFlavor生成的task, 从而我们直接引用到该task的编译成java后的class,为了解决该问题, android对象提供了三个Collection属性:

applicationVariants(只适用于app plugin)

libraryVariants(只适用于library plugin)

testVariants(两个plugin都适用)

这三个collection中的任意一个都会触发生成所有对应的task。

android.applicationVariants.each { variant ->
        ....
    }

http://wiki.jikexueyuan.com/project/android-gradle-guide/advanced-build-customization.html

通过这些变量和方法可定制对应的功能。

官方详细文档:http://tools.android.com/tech-docs/new-build-system/user-guide

八.Gradle自定义Task和Plugin

Gradle本身只是一个架子,真正起作用的是Task和Plugin。要真正了解Task和Plugin的工作机制并熟练运用,学会自定义Task类型和Plugin是大有裨益的。

1. 自定义Task。

Gradle中的Task要么是由不同的Plugin引入的,要么是我们自己在build.gradle文件中直接创建的。在默认情况下,我们所创建的Task是DefaultTask类型,该类型是一个非常通用的Task类型,而在有些时候,我们希望创建一些具有特定功能的Task,比如Copy和Jar等。

Demo: 9-custom-task, 分为三部分,1,在build.gradle中直接定义Task,2.在当前工程中定义Task,3.在单独的项目中定义Task,将其上传maven库中,其他项目来引用。

2. 自定义Plugin。

在Plugin中,我们可以向Project中加入新的Task,定义configurations和property等。我们3种方法可以自定义Plugin,这些方法和自定义Task类型的3种方法相似。

Demo:10-custom-plugin 每一个自定义的Plugin都需要实现Plugin<T>接口,事实上,除了给Project编写Plugin之外,我们还可以为其他Gradle类编写Plugin。该接口定义了一个apply()方法,在该方法中,我们可以操作Project,比如向其中加入Task,定义额外的Property等。

九.通过Gradle发布jar,aar,plugin到JCenter和Maven Central

1. 发布到jcenter中

http://www.cnblogs.com/qianxudetianxia/p/4322331.html

2. 发布到maven central

http://my.oschina.net/specialcyci/blog/371352#OSC_h3_2

3. 发布到本地maven库

apply plugin:‘maven’

uploadArchives{

repositories.mavenDeployer{

   repository(url: ‘file:../lib’)

}

}

使用时

buildScript{

       repositories{

maven{

  url ‘file:../lib’}}}

十. 新的构建方案

  1. facebook buck https://buckbuild.com/

  2. http://www.voidcn.com/blog/u014077888/article/p-4146683.html

十一. 例子

helloworld.groovy


//1. 打印helloworld
println 'helloworld'
//2. 类型定义
def value = "hello"
println value
//3. 查看类型是什么
println value.class
def value1 = 4
println value1.class 
def value2 = 'a'
println value2.class


//4. groovy for循环,int无需定义类型
def repeat(val) {
   /*for(i = 0; i < 5; i++) {
      println val;
   }*/
   //groovy中的范围, 0..5 是一个集合
   /*for(i in 0..5) {
      println val;
   }*/
   //将范围改为排除
   for(i in 0..<5) {
      println val
   }
}
repeat("helloworld")

//5. 默认参数
def repeat1(val, repeat=5) {
   for(i in 0..<repeat) {
      println val
   }
}
repeat1("hello", 3)
repeat1("world")

//6. groovy 集合
def coll = ["Groovy", "Java", "Ruby"]
println coll.class
assert  coll instanceof Collection
assert coll instanceof ArrayList

//7. 插入符号, 集合操作。
coll.add("Python")
coll << "Smalltalk"
coll[5] = "Perl"

println coll
//8. * 操作符
def upper = ["Java", "Groovy"]*.toUpperCase()
println upper

//9. 映射
def hash = [name:"Andy", "VPN-#":45]
assert hash.getClass() == java.util.LinkedHashMap

hash.put("id", 23)
assert hash.get("name") == "Andy"

//或者用. 设置 ,读取
hash.dob = "01/29/76"

println hash.name
println hash['name']
println hash

//11. 闭包
coll.each{ println it}
coll.each{name -> 
   println name
}

//12. 定义闭包
def excite = {word ->
   return "${word}!!!"
}

println excite('helloworld')

//默认参数为it
def greeting = { "Hello, ${it}!" }
println greeting('hello')

//当函数的最后一个参数是闭包的话,可以省略()
def testClosure(int a1, String b1, Closure closure) {
   closure() //调用闭包
}

testClosure(3, "test", {
   println 'I am a closure'
   })
//如android gradle中的
/*doLast({
   println'Hello world!'
})*/
///////groovy 深入//////

//查看变量作用范围
//run作用域
/*def x = 1
def printx() {
   println x
}

printx()
*/

//全局作用域
y = 1
def printy() {
   println y
}
开发技能