【Android Gradle】Gradle系列

环境配置

Android Studio 中运行 groovy 程序

一. 文章学习笔记

Gradle 系列 (一)、Gradle相关概念理解,Groovy基础

Gradle 系列 (二)、Gradle 技术探索

1. Gradle 系列 (一)、Gradle相关概念理解,Groovy基础

1.1 Gradle 是什么

Gradle 是一个开源的自动化构建工具,专注于灵活性和性能。Gradle 构建脚本是使用 Groovy 或 Kotlin DSL 编写的。
Gradle 是 Android 的默认构建工具,Android 项目这么多东西,既有我们自己写的 java、kotlin、C++、Dart 代码,也有系统自己的 java、C,C++ 代码,还有引入的第三方代码,还有多媒体资源,这么多代码、资源打包成 APK 文件肯定要有一个规范,干这个活的就是我们熟悉的 gradle 了,总而言之,Gradle就是一个帮我们打包 APK 的工具
在这里插入图片描述

1.2 DSL是什么

domain specific language,中文翻译即领域特定语言
解决特定领域的专有问题
比如 Java 语言希望能做 Android 开发,又希望能做后台开发,它具有横向扩展的特性。而 DSL 具有纵向深入解决特定领域专有问题的特性。
Gradle 的 DSL 目前支持两种语言的格式,Groovy 和 Kotlin,Kotlin 格式的 DSL 是在 5.0 引入的

1.3 什么是 Groovy

Groovy 是基于 JVM 的脚本语言,它是基于Java扩展的动态语言
基于 JVM 的语言有很多种,如:Groovy,Kotlin,Java,Scala等等,他们都拥有一个共同的特性:最终都会编译生成 Java 字节码文件并在 JVM 上运行

1.4 Grovy基础语法

支持动态类型,使用 def 关键字来定义一个变量
//Java 中,我们一般会这么定义
int age = 16
String name = "erdai"

//Groovy 中,我们可以这样定义,在变量赋值后,Groovy 编译器会推断出变量的实际类型
def age = 16
def name = 'erdai'
不用写 ; 号
没有基本数据类型了,全是引用类型
方法变化
  1. 使用 def 关键字定义一个方法,方法不需要指定返回值类型,参数类型,方法体内的最后一行会自动作为返回值,而不需要return关键字
  2. 方法调用可以不写 () ,最好还是加上 () 的好,不然可读性不好
  3. 定义方法时,如果参数没有返回值类型,我们可以省略 def,使用 void 即可
  4. 实际上不管有没有返回值,Groovy 中返回的都是 Object 类型
  5. 类的构造方法避免添加 def 关键字
def sum(a,b){
    a + b
}
def sum = sum(1,2) //还可以写成这样,但是可读性不好 def sum = sum 1,2 
println(sum)

//打印结果
3

//如果方法没有返回值,我们可以这样写:
void doSomething(param1, param2) {
  
}

//类的构造方法,避免添加 def 关键字
class MyClass {
    MyClass() {
      
    }
}

字符串变化

可扩展字符串的含义,可扩展字符串就是字符串里面可以引用变量,表达式等等
1 、单引号 ‘’ 定义的字符串为不可扩展字符串
2 、双引号 “” 定义的字符串为可扩展字符串,可扩展字符串里面可以使用 ${} 引用变量值,当 {} 里面只有一个变量,非表达式时,{}也可以去掉
3 、三引号 ‘’’ ‘’’ 定义的字符串为输出带格式的不可扩展字符串

不用写 get 和 set 方法
  1. 在我们创建属性的时候,Groovy会帮我们自动创建 get 和 set 方法
  2. 当我们定义了一个属性的 get 方法,而没有定义这个属性,默认这个属性只读
  3. 如果我们不想调用这个特殊的 get 方法时则可以使用 .@ 直接域访问操作符访问属性本身
//情况1:在我们创建属性的时候,Groovy会帮我们自动创建 get 和 set 方法
class People{
    def name
    def age
}

def people = new People()
people.name = 'erdai'
people.age = 19
println "姓名: $people.name 年龄: $people.age"
//打印结果
姓名: erdai 年龄: 19
  
//情况2 当我们定义了一个属性的 get 方法,而没有定义这个属性,默认这个属性只读
//我们修改一下People类
class People{
    def name
    def getAge(){
        12
    }
}

def people = new People()
people.name = 'erdai'
people.age = 19
println "姓名: $people.name 年龄: $people.age"
//运行一下代码 打印结果报错了,如下:
Caught: groovy.lang.ReadOnlyPropertyException: Cannot set readonly property: age for class: variable.People
//大概错误意思就是我们不能修改一个只读的属性
  
//情况3: 如果我们不想调用这个特殊的 get 方法时则可以使用 .@ 直接域访问操作符访问属性本身
class People{
    def name
    def age
    
    def getName(){
        "My name is $name"
    }
}
//这里使用了命名的参数初始化和默认的构造器创建people对象,后面会讲到
def people = new People(name: 'erdai666')
people.age = 19
def myName = people.@name

//打印值
println myName
println "姓名: $people.name 年龄: $people.age"
  
//打印结果
erdai666
姓名: My name is erdai666 年龄: 19
//看到区别了吗?使用 people.name 则会去调用这个属性的get方法,而 people.@name 则会访问这个属性本身
Class 是一等公民,所有的 Class 类型可以省略 .Class
//定义一个Test类
class Test{ 

}

//定义一个测试class的方法,从前面的语法我们知道,方法的参数类型是可以省略的
def testClass(myClass){
    
}

//测试
testClass(Test.class)
testClass(Test)
== 和 equals

== 就相当于 Java 的 equals,如果需要比较两个对象是否是同一个,需要使用 .is()

使用 assert 来设置断言,当断言的条件为 false 时,程序将会抛出异常
支持 ** 次方运算符
简洁的三元表达式 ?:
简洁的非空判断 ?.
强大的 Switch
def result = 'erdai666'
switch (result){
    case [1,2,'erdai666']:
        println "匹配到了result"
        break
    default:
        println 'default'
        break
}
//打印结果
匹配到了result
判断是否为 null 和 非运算符

在 Groovy 中,所有类型都能转成布尔值,比如 null 就相当于0或者相当于false,其他则相当于true

//在 Java 中,我们会这么用
if (name != null && name.length > 0) {
  
}

//在 Groovy 中,可以这么用,如果name为 null 或 0 则返回 false,否则返回true
if(name){
  
}

//非运算符 erdai 这个字符串为非 null ,因此为true,而 !erdai 则为false
assert (!'erdai') = false

可以使用 Number 类去替代 float、double 等类型,省去考虑精度的麻烦
默认是 public 权限
使用命名的参数初始化和默认的构造器
//定义一个people
class People{
    def name
    def age
}

//我们可以通过以下几种方式去实例化一个对象,注意我们People类里面没有写任何一个构造方法哦
def people1 = new People()
def people1 = new People(age: 15)
def people2 = new People(name: 'erdai')
def people3 = new People(age: 15,name: 'erdai')

使用 with 函数操作同一个对象的多个属性和方法
异常捕获
//在 java 中我们会这样写
try {
    // ...
} catch (Exception e) {
    // do something
}

//在 Groovy 中,我们可以这样写
try {
    // ...
} catch (any) {
    // do something
}

1.5 Groovy 闭包

定义

Groovy 中的闭包是一个开放的、匿名的代码块,它可以接受参数、返回值并将值赋给变量

声明

1、闭包基本的语法结构:外面一对大括号,接着是申明参数,参数类型可省略,在是一个 -> 箭头号,最后就是闭包体里面的内容
2、闭包也可以不定义参数,如果闭包没定义参数的话,则隐含有一个参数,这个参数名字叫 it

//1
{ params ->
   //do something
}

//2
{ 
   //do something
}

调用

1、闭包可以通过 .call 方法来调用

2、闭包可以直接用括号+参数来调用

闭包中的关键变量this、owner 和 delegate
  • getThisObject() 方法 和 thisObject 属性等同于 this
    this 永远指向定义该闭包最近的类对象,就近原则,定义闭包时,哪个类离的最近就指向哪个,我这里的离得近是指定义闭包的这个类,包含内部类
  • getOwner() 方法 等同于 owner
    owner 永远指向定义该闭包的类对象或者闭包对象,顾名思义,闭包只能定义在类中或者闭包中
  • getDelegate() 方法 等同于 delegate
    delegate 和 owner 是一样的,我们在闭包的源码中可以看到,owner 会把自己的值赋给 delegate,但同时 delegate 也可以赋其他值
    this 和 owner 默认是只读的,我们外部修改不了它,这点在源码中也有体现,但是可以对 delegate 进行操作
def outerClosure = {
    def innerClosure = {
        println "this: " + this
        println "owner: " + owner
        println "delegate: " + delegate
    }
    innerClosure.call()
  	
}
println outerClosure
outerClosure.call()

//打印结果如下
variable.GroovyGrammar$_run_closure4@64beebb7
this: variable.GroovyGrammar@5b58ed3c
owner: variable.GroovyGrammar$_run_closure4@64beebb7
delegate: variable.GroovyGrammar$_run_closure4@64beebb7
//结果证明 this 指向了当前GroovyGrammar这个脚本类对象 owner 和 delegate 都指向了 outerClosure 闭包对象
闭包委托策略

闭包的默认委托策略是 OWNER_FIRST,也就是闭包会先从 owner 上寻找属性或方法找不到则在 delegate 上寻找

  • 其他策略
    OWNER_FIRST:默认策略,首先从 owner 上寻找属性或方法,找不到则在 delegate 上寻找
    DELEGATE_FIRST:和上面相反,首先从 delegate 上寻找属性或者方法,找不到则在 owner 上寻找
    OWNER_ONLY:只在 owner 上寻找,delegate 被忽略
    DELEGATE_ONLY:和上面相反,只在 delegate 上寻找,owner 被忽略
    TO_SELF:高级选项,让开发者自定义策略,必须要自定义实现一个 Closure 类,一般我们这种玩家用不到
//创建一个香蕉类
class Banana{
    def name
}

//创建一个橘子类
class Orange{
    def name
}

//定义一个香蕉对象
def banana = new Orange(name: '香蕉')
//定义一个橘子对象
def orange = new Orange(name: '橘子')
//定义一个闭包对象
def closure = {
    //打印值
    println delegate.name
}
//调用闭包
closure.call()

//运行一下,发现结果报错了,如下
Caught: groovy.lang.MissingPropertyException: No such property: name for class: variable.GroovyGrammar
//大致意思就是GroovyGrammar这个脚本类对象没有这个 name 对象

//修改闭包的delegate
closure.delegate = orange
//我们在运行一下,打印结果:
橘子

1.6 Groovy数据结构

数组

as关键字

def groovyArray = ["Java", "Groovy", "Android"] as String[]
列表
def list2 = ['erdai666', 1, true]
def list4 = [1, 2, 3] as LinkedList

list.add(20)
list[0] = 100
list.remove(0)
list.find {
    println it
}
Map
def map = [a: 1, 'b': true, "c" : "Groovy", '''d''' : '''ddd''']
// 元素访问操作
println map.a
println map['b']
println map.get('c')
// 添加和修改元素
map.put('key','value')
map['key'] = "value"
Range
def range = 1..10
//如果不想包含最后一个元素
def range1 = 1..<10

2. Gradle 系列 (二)、Gradle 技术探索

2.1 Gradle 构建流程

初始化阶段,配置阶段,执行阶段
在这里插入图片描述
在这里插入图片描述

初始化阶段
  • 执行 settings.gradle 脚本,构建 Project 对象
  • 如何引用任何位置下的工程
    Settings 是一个对象,include 就是 Settings 对象下的一个方法。其他API见链接 添加链接描述
include ':test'
//手动设置 :test 模块的项目目录(即源码和 build.gradle 所在目录)为指定路径。
project(':test').projectDir = file('当前工程的绝对路径')

在这里插入图片描述
AAR 和源码的快速切换

//步骤一:在 settings.gradle 中引入源码工程
include ':test'
project(':test').projectDir = file('当前工程的绝对路径')

//步骤二:在根 build.gradle 下进行如下配置
// 步骤二:在根 build.gradle 下进行如下配置
// allprojects 表示对当前项目及其所有子项目生效
allprojects {
    // 遍历所有配置(compile, runtime 等)
    configurations.all {
        // resolutionStrategy 定义了依赖解析策略
        resolutionStrategy {
            // dependencySubstitution 允许替换依赖项
            dependencySubstitution {
                // 将所有对 com.dream:test 的依赖替换为本地项目 :test
                substitute module("com.dream:test") with project(':test')
            }
        }
    }
}
配置阶段

Gradle 配置阶段主要就是解析 Project 对象(build.gradle 脚本文件),构建 Task 有向无环图
配置阶段会执行的代码:除 Task 的 Action 中编写的代码都会被执行(Action 中的代码(即 doFirst、doLast 或 << 操作符) 是在 执行阶段 才运行的代码。)。如:
1、build.gradle 中的各种语句
2、Task 配置段语句
配置阶段完成后,整个工程的 Task 依赖关系都确定了,我们可以通过 Gradle 对象的 getTaskGraph 方法访问 Task ,对应的类为 TaskExecutionGraph

执行阶段

Gradle 执行阶段主要就是执行 Task 及其依赖的 Task

2.2 Gradle生命周期 Hook 点

在这里插入图片描述
代码示例:打印构建时间

pluginManagement {
    repositories {
        google {
            content {
                includeGroupByRegex("com\\.android.*")
                includeGroupByRegex("com\\.google.*")
                includeGroupByRegex("androidx.*")
            }
        }
        maven { url 'https://jitpack.io' }
        mavenCentral()
        gradlePluginPortal()
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        maven { url 'https://jitpack.io' }
        mavenCentral()
    }
}

def beginOfSetting = System.currentTimeMillis()//配置阶段开始时间

gradle.projectsLoaded {
    def totalTime = System.currentTimeMillis() - beginOfSetting
    println "Total build time: ${totalTime} ms"
}


rootProject.name = "TreasureBox"
include ':app'
include ':lib'

2.3 Project

API

getRootProject 方法:获取根 Project 对象
getRootDir 方法:获取根目录文件夹路径
getBuildDir 方法:获取当前 Project 的 build 文件夹路径
getParent 方法:获取当前父 Project 对象
getAllprojects 方法:获取当前 Project 及其子 Project 对象,返回值是一个 Set 集合
闭包的形式

allprojects {
    println it
}

//打印结果
> Configure project :
root project 'GradleDemo'
project ':app'

//我们通常会使用闭包的语法在根 build.gradle 下进行相关的配置,如下:
allprojects {
    repositories {
        google()
        mavenCentral()
    }
}

getSubprojects 方法:获取当前 Project 下的所有子 Project 对象,返回值是一个 Set 集合
apply 系列方法:引用插件

//引用第三方插件
apply plugin: 'com.android.application'
//引用脚本文件插件
apply from: 'config.gradle'

configurations 闭包:编写 Project 一些相关的配置,如全局移除某个依赖

configurations {
    all*.exclude group: '组名', module: '模块名'
}

project 系列方法:指定工程实例,然后在闭包中对其进行相关的配置

project("app") {
    apply plugin: 'com.android.application'
}

扩展属性 todo
文件操作API
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值