Gradle 介绍
Gradle 是一个基于 JVM 的新一代构建工具, 可以用于自动化自定义有序的步骤来完成代码的编译, 测试和打包等工作, 让重复的步骤变得简单, 用于实现项目自动化, 是一种可编程的工具, 你可以用代码来控制构建流程最终生成可交付的软件. 构建工具可以帮助你创建一个重复的, 可靠的, 无需手动介入的, 不依赖于特定操作系统和 IDE 的构建
Gradle 优势
1.Gradle 结合 Ant 和 Maven 等构建工具的最佳特性. 它有着约定优于配置的方法, 强大的依赖管理, 它的构建脚本使用 Groovy 或 Kotlin 编写
2.Gradle 有非常良好的拓展性. 如果你想要在多个构建或者项目中分享可重用代码, Gradle 的插件会帮助你实现. 将 Gradle 插件应用于你的项目中, 它会在你的项目构建过程中提供很多帮助: 为你的添加项目的依赖的第三方库, 为你的项目添加有用的默认设置和约定(源代码位置, 单元测试代码位置). 其中 Android Gradle 插件继承 Java Gradle 插件
3.Gradle 可以使用 Groovy 来实现构建脚本, Groovy 是基于 Jvm 一种动态语言, 它的语法和 Java 非常相似并兼容 Java, 因此你无需担心学习 Groovy 的成本. Groovy 在 Java 的基础上增加了很多动态类型和灵活的特性, 比起 xml,Gradle 更具有表达性和可读性.
4.Gradle 提供了可配置的可靠的依赖管理方案. 一旦依赖的库被下载并存储到本地缓存中, 我们的项目就可以使用了. 依赖管理很好的实现了在不同的平台和机器上产生相同的构建结果.
5.Gradle 可以为构建你的项目提供引导和默认值, 如果你使用这种约定, 你的 Gradle 构建脚本不会有几行.
6.Gradle Wrapper 是对 Gradle 的包装, 它的作用是简化 Gradle 本身的下载, 安装和构建, 比如它会在我们没有安装 Gradle 的情况下, 去下载指定版本的 Gradle 并进行构建. Gradle 的版本很多, 所以有可能出现版本兼容的问题, 这时就需要 Gradle Wrapper 去统一 Gradle 的版本, 避免开发团队因为 Gradle 版本不一致而产生问题.
7.Gradle 可以和 Ant,Maven 和 Ivy 进行集成, 比如我们可以把 Ant 的构建脚本导入到 Gradle 的构建中
8.Gradle 显然无法满足所有企业级构建的所有要求, 但是可以通过 Hook Gradle 的生命周期, 来监控和配置构建脚本.
9. 社区的支持和推动
想了解更多有关于 Gradle 知识的小伙伴, 小编给准备了一期视频专门讲解 Gradle: 揭秘 Android 开发效率提升十倍的利器 - Gradle 提取码: 3m8g
更多资料分享欢迎 Android 工程师朋友们加入安卓开发技术进阶互助: 856328774 免费提供安卓开发架构的资料 (包括 Fultter, 高级 UI, 性能优化, 架构师课程, NDK,Kotlin, 混合式开发(ReactNative+Weex) 和一线互联网公司关于 Android 面试的题目汇总.
gradle 入门
gradle 这个基于 Groovy 的 DSL,DSL(Domain Specifc Language)意为领域特定语言, 只用于某个特定的领域. 我们只要按照 Groovy 的 DSL 语法来写, 就可以轻松构建项目
task:
task(任务)和 action(动作)是 Gradle 的重要元素. task 代表一个独立的原子性操作, 比如复制一个文件, 编译一次 Java 代码, 这里我们简单的定义一个名为 hello 的任务. doLast 代表 task 执行的最后一个 action, 通俗来讲就是 task 执行完毕后会回调 doLast 中的代码
- task hello {
- doLast {
- println 'Hello world!'
- }
- }
也可以写成
- task hello <<{
- println 'Hello world!'
- }
操作符<< 是 doLast 方法的快捷版本
Gradle 的任务
Gradle 的任务, 包括创建任务, 任务依赖, 动态定义任务和任务的分组和描述
1 创建任务
1.1 直接用任务名称创建.
- def Task hello=task(hello)
- hello.doLast{
- println "hello world"
- }
1.2 任务名称 + 任务配置创建
- def Task hello=task(hello,group:BasePlugin.BUILD_GROUP)
- hello.doLast{
- println "hello world"
- }
1.3.TaskContainer 的 create 方法创建.
- tasks.create(name: 'hello') << {
- println "hello world"
- }
1.4 通过上面 DSL 语法创建
任务依赖
任务依赖会决定任务运行的先后顺序, 被依赖的任务会在定义依赖的任务之前执行. 创建任务间的依赖关系如下所示.
- task hello << {
- println 'Hello world!'
- }
- task go(dependsOn: hello) << {
- println "go for it"
- }
在 hello 任务的基础上增加了一个名为 go 的任务, 通过 dependsOn 来指定依赖的任务为 hello, 因此 go 任务运行在 hello 之后.
3 . 动态定义任务
动态定义任务指的是在运行时来定义任务的名称
- 3.times {number ->
- task "task$number" <<{
- println "task $number"
- }
- }
times 是 Groovy 在 java.lang.Number 中拓展的方法, 是一个定时器. 3.times 中循环创建了三个新任务, 隐式变量 number 的值为 0,1,2, 任务的名称由 task 加上 number 的值组成, 达到了动态定义任务的目的.
运行 gradle -q task0 构建脚本
任务的分组和描述
Gradle 有任务组的概念, 可以为任务配置分组和描述, 以便于更好的管理任务, 拥有良好的可读性.
- task hello {
- group = 'build'
- description = 'hello world'
- doLast {
- println "任务分组: ${group}"
- println "任务描述: ${description}"
- }
- }
- task go(dependsOn: hello) << {
- println "go for it"
- }
Gradle 日志级别
级别 用于
ERROR 错误消息
QUIET 重要的信息消息
WARNING 警告消息
LIFECYCLE 进度信息消息
INFO 信息性消息
DEBUG 调试消息
前面我们通过 gradle -q + 任务名称来运行一个指定的 task, 这个 q 是命令行开关选项, 通过开关选项可以控制输出的日志级别.
开关选项 输出日志级别
无日志选项 LIFECYCLE 及更高级别
-q 或者 --quiet QUIET 及更高级别
-i 或者 --info INFO 及更高级别
-d 或者 --debug DEBUG 及更高级别
Gradle 命令行
略
Gradle 的语法
1. 声明变量
Groovy 中用 def 关键字来定义变量, 可以不指定变量的类型, 默认访问修饰符是 public.
- def a = 1;
- def int b = 1;
- def c = "hello world";
2. 方法
方法使用返回类型或 def 关键字定义, 方法可以接收任意数量的参数, 这些参数可以不申明类型, 如果不提供可见性修饰符, 则该方法为 public, 如果指定了方法返回类型, 可以不需要 def 关键字来定义方法, 如果不使用 return , 方法的返回值为最后一行代码的执行结果.
用 def 关键字定义方法.
- task method <<{
- add (1,2)
- minus 1,2 //1
- }
- def add(int a,int b) {
- println a+b //3
- }
- int minus(a,b) {
- return a-b
- }
3. 类
Groovy 类非常类似于 Java 类.
- task method <<{
- def p = new Person()
- p.increaseAge 5
- println p.age
- }
- class Person {
- String name
- Integer age =10
- def increaseAge(Integer years) {
- this.age += years
- }
- }
Groovy 类与 Java 类有以下的区别:
默认类的修饰符为 public.
没有可见性修饰符的字段会自动生成对应的 setter 和 getter 方法.
类不需要与它的源文件有相同的名称, 但还是建议采用相同的名称.
4. 语句
(1) 断言
Groovy 断言和 Java 断言不同, 它一直处于开启状态, 是进行单元测试的首选方式.
- task method <<{
- assert 1+2 == 6
- }
(2)for 循环
Groovy 支持 Java 的 for(int i=0;i<N;i++)和 for(int i :array)形式的循环语句, 另外还支持 for in loop 形式, 支持遍历范围, 列表, Map, 数组和字符串等多种类型
- // 遍历范围
- def x = 0
- for ( i in 0..3 ) {
- x += i
- }
- assert x == 6
- // 遍历列表
- def x = 0
- for ( i in [0, 1, 2, 3] ) {
- x += i
- }
- assert x == 6
- // 遍历 Map 中的值
- def map = ['a':1, 'b':2, 'c':3]
- x = 0
- for ( v in map.values() ) {
- x += v
- }
- assert x == 6
(3)switch 语句
- task method <<{
- def x = 16
- def result = ""
- switch ( x ) {
- case "ok":
- result = "found ok"
- case [1, 2, 4, 'list']:
- result = "list"
- break
- case 10..19:
- result = "range"
- break
- case Integer:
- result = "integer"
- break
- default:
- result = "default"
- }
- assert result == "range"
- }
5. 数据类型
Groovy 中的数据类型主要有以下几种:
Java 中的基本数据类型
Groovy 中的容器类
闭包
(1)字符串
在 Groovy 种有两种字符串类型, 普通字符串 String(java.lang.String)和插值字符串 GString(groovy.lang.GString)
- def name = 'Android 进阶之光'
- println "hello ${name}"
- println "hello $name"
- task method <<{
- def name = '''Android 进阶之光
- Android 进阶解密
- Android 进阶?'''
- println name
- }
- (2)List
Groovy 没有定义自己的集合类, 它在 Java 集合类的基础上进行了增强和简化. Groovy 的 List 对应 Java 中的 List 接口, 默认的实现类为 Java 中的 ArrayList.
- def number = [1, 2, 3]
- assert number instanceof List
- def linkedList = [1, 2, 3] as LinkedList
- assert linkedList instanceof java.util.LinkedList
- task method <<{
- def number = [1, 2, 3, 4]
- assert number [1] == 2
- assert number [-1] == 4 //1
- number << 5 //2
- assert number [4] == 5
- assert number [-1] == 5
- }
- def name = [one: '魏无羡', two: '杨影枫', three: '张无忌']
- assert name['one'] == '魏无羡'
- assert name.two == '杨影枫'
注释 1 处的索引 - 1 是列表末尾的第一个元素. 注释 2 处使用<<运算符在列表末尾追加一个元素
其他
- String a = '23'
- int b = a as int
- def c = a.asType(Integer)
- assert c instanceof java.lang.Integer
- Gradle Files
我们在 AS 中用到的 Gradle 其实应该被叫做 Android Gradle Plugin, 也就是安卓项目上的 gradle 插件;
Gradle 插件会有版本号, 每个版本号又对应有一个或一些 Gradle 发行版本(一般是限定一个最低版本), 也就是我们常见的类似 gradle-3.1-all.zip 这种东西;
如果这两个版本对应不上了, 那你的工程构建的时候就会报错.
Android Studio 3.0 之后自动将插件版本升级到 3.0.0, 所以我们也需要对应地把 Gradle 升级到 4.1 才行
另外, Android Gradle Plugin 又会跟 Android SDK BuildTool 有关联, 因为它还承接着 AndroidStudio 里的编译相关的功能, 这也是我们要在项目的 local.properties 文件里写明 Android SDK 路径, 在 build.gradle 里注明 buildToolsVersion 的原因.
所以 Android Gradle Plugin 本质上就是 一个 AS 的插件, 它一边调用 Gradle 本身的代码和批处理工具来构建项目, 一边调用 Android SDK 的编译, 打包功能, 从而让我们能够顺畅地在 AS 上进行开发.
最基础的文件配置
在 Android studio 中, 没有类似于 Eclipse 工作空间 (Workspace) 的概念, 而是提出了 Project 和 Module 这两个概念, Project 是最顶级的结构单元, 然后就是 Module, 一个 Project 可以有多个 Module. 目前, 主流的大型项目结构基本都是多 Module 的结构, 这类项目一般是按功能划分的. 一个 Project 是由一个或多个 Module 组成, 尽量让各模块处于同一项目之中, 此时彼此之间具有互相依赖的关联关系, 在一般情况下, Android 是默认单 Project 单 Module 的, 这时 Project 和 Module 合二为一, 在没有修改存储路径的时候, 显然 Project 对 Module 具有强约束作用.
我们可以简单的理解为: 一个 Project 代表一个完整的 App,Module 表示 App 中的一些依赖库或独立开发的模块. 比如可以新建一个 library 做为 module, 然后在主 App 上点右键 open module setting 的 Dependencies 中添加一个模块依赖. 然后主 App 中就可以使用 module 中的类了
一个项目有一个 setting.gradle, 包括一个顶层的 Project 的 build.gradle 文件, 每个 Module 都有自己的一个 build.gradle 文件, Android studio 默认创建一个 App 的 Module.
setting.gradle: 这个 setting 文件定义了哪些 module 应该被加入到编译过程, 对于单个 module 的项目可以不用需要这个文件, 但是对于 multimodule 的项目我们就需要这个文件, 否则 gradle 不知道要加载哪些项目. 这个文件的代码在初始化阶段就会被执行.
顶层的 project 的 build.gradle 文件的配置最终会被应用到所有项目中.
buildscript: 定义了 Android 编译工具的类路径. repositories 中, jCenter 是一个著名的 Maven 仓库.
dependencies {
classpath 'com.android.tools.build:gradle:3.1.0' 这里配置 gradle 的插件来编译 gradle 文件, 同时也可以配置其他插件, gradle 插件的版本需要与 gradle-wrapper.properties 中 gradle 版本对应
}
allprojects: 中定义的属性会被应用到所有 moudle 中, 但是为了保证每个项目的独立性, 我们一般不会在这里面操作太多共有的东西.
apply plugin: 第一行代码应用了 Android 程序的 gradle 插件, 作为 Android 的应用程序, 这一步是必须的, 因为 plugin 中提供了 Android 编译, 测试, 打包等等的所有 task.
每个项目单独的 build.gradle: 针对每个 moudle 的配置, 如果这里的定义的选项和顶层 build.gradle 定义的相同, 后者会被覆盖.
Android: 这是编译文件中最大的代码块, 关于 Android 的所有特殊配置都在这里, 这就是又我们前面的声明的 plugin 提供的.
defaultConfig 就是程序的默认配置, 注意, 如果在 AndroidMainfest.xml 里面定义了与这里相同的属性, 会以这里的为主.
这里最有必要要说明的是 applicationId 的选项: 在我们曾经定义的 AndroidManifest.xml 中, 那里定义的包名有两个用途: 一个是作为程序的唯一识别 ID, 防止在同一手机装两个一样的程序; 另一个就是作为我们 R 资源类的包名. 在以前我们修改这个 ID 会导致所有用引用 R 资源类的地方都要修改. 但是现在我们如果修改 applicationId 只会修改当前程序的 ID, 而不会去修改源码中资源文件的引用.
buildTypes: 定义了编译类型, 针对每个类型我们可以有不同的编译配置, 不同的编译配置对应的有不同的编译命令. 默认的有 debug,release 的类型.
可以通过配置 buildConfigField 设置一些 key-value 对, 这些 key-value 对在不同编译类型的 apk 下的值不同, 比如我们可以为 debug 和 release 两种环境定义不同的服务器, 还可以通过 manifestPlaceholders 修改 manifest 里面 meta-data 的值
- buildTypes {
- debug {
- minifyEnabled false
- shrinkResources false
- manifestPlaceholders = [aaaa: "122222423524123139666"]
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- buildConfigField("String","URL","http://www.baidu.com")
- }
- release {
- minifyEnabled true
- shrinkResources true
- signingConfig signingConfigs.release
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- manifestPlaceholders = [aaaa: "12222133655466889666"]
- buildConfigField("String","URL","http://www.google.com")
- }
- }
- String host = BuildConfig.url;// 这里取 buildConfigField 的值
- //manifest 里面配置
- <meta-data
- Android:name="JPUSH_APPKEY"
- Android:value="${aaaa}" />
dependencies: 是属于 gradle 的依赖配置. 它定义了当前项目需要依赖的其他库.
Gradle Wrapper
radle 不断的在发展, 新的版本难免会对以往的项目有一些向后兼容性的问题, 这个时候, gradle wrapper 就应运而生了.
gradlw wrapper 包含一些脚本文件和针对不同系统下面的运行文件. wrapper 有版本区分, 但是并不需要你手动去下载, 当你运行脚本的时候, 如果本地没有会自动下载对应版本文件.
在不同操作系统下面执行的脚本不同, 在 Mac 系统下执行./gradlew ..., 在 Windows 下执行 gradle.bat 进行编译.
如果你是直接从 eclipse 中的项目转换过来的, 程序并不会自动创建 wrapper 脚本, 我们需要手动创建. 在命令行输入以下命令即可
gradle wrapper --gradle-version 2.4
它会创建如下目录结构:
想了解更多有关于 Gradle 知识的小伙伴, 小编给准备了一期视频专门讲解 Gradle: 揭秘 Android 开发效率提升十倍的利器 - Gradle 提取码: 3m8g
更多资料分享欢迎 Android 工程师朋友们加入安卓开发技术进阶互助: 856328774 免费提供安卓开发架构的资料 (包括 Fultter, 高级 UI, 性能优化, 架构师课程, NDK,Kotlin, 混合式开发(ReactNative+Weex) 和一线互联网公司关于 Android 面试的题目汇总.
来源: http://www.jianshu.com/p/9f9e543f3a2b