【Android Gradle】Gradle系列

文章目录

  • 环境配置
  • [一. 文章学习笔记](#一. 文章学习笔记)
    • [1. Gradle 系列 (一)、Gradle相关概念理解,Groovy基础](#1. Gradle 系列 (一)、Gradle相关概念理解,Groovy基础)
      • [1.1 Gradle 是什么](#1.1 Gradle 是什么)
      • [1.2 DSL是什么](#1.2 DSL是什么)
      • [1.3 什么是 Groovy](#1.3 什么是 Groovy)
      • [1.4 Grovy基础语法](#1.4 Grovy基础语法)
        • [支持动态类型,使用 def 关键字来定义一个变量](#支持动态类型,使用 def 关键字来定义一个变量)
        • [不用写 ; 号](#不用写 ; 号)
        • 没有基本数据类型了,全是引用类型
        • 方法变化
        • 字符串变化
        • [不用写 get 和 set 方法](#不用写 get 和 set 方法)
        • [Class 是一等公民,所有的 Class 类型可以省略 .Class](#Class 是一等公民,所有的 Class 类型可以省略 .Class)
        • [== 和 equals](#== 和 equals)
        • [使用 assert 来设置断言,当断言的条件为 false 时,程序将会抛出异常](#使用 assert 来设置断言,当断言的条件为 false 时,程序将会抛出异常)
        • [支持 ** 次方运算符](#支持 ** 次方运算符)
        • [简洁的三元表达式 ?:](#简洁的三元表达式 ?:)
        • [简洁的非空判断 ?.](#简洁的非空判断 ?.)
        • [强大的 Switch](#强大的 Switch)
        • [判断是否为 null 和 非运算符](#判断是否为 null 和 非运算符)
        • [可以使用 Number 类去替代 float、double 等类型,省去考虑精度的麻烦](#可以使用 Number 类去替代 float、double 等类型,省去考虑精度的麻烦)
        • [默认是 public 权限](#默认是 public 权限)
        • 使用命名的参数初始化和默认的构造器
        • [使用 with 函数操作同一个对象的多个属性和方法](#使用 with 函数操作同一个对象的多个属性和方法)
        • 异常捕获
      • [1.5 Groovy 闭包](#1.5 Groovy 闭包)
      • [1.6 Groovy数据结构](#1.6 Groovy数据结构)
    • [2. Gradle 系列 (二)、Gradle 技术探索](#2. Gradle 系列 (二)、Gradle 技术探索)
    • [2.2 Gradle生命周期 Hook 点](#2.2 Gradle生命周期 Hook 点)

环境配置

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 复制代码
//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 关键字
java 复制代码
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 方法时则可以使用 .@ 直接域访问操作符访问属性本身
java 复制代码
//情况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
java 复制代码
//定义一个Test类
class Test{ 

}

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

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

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

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

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

java 复制代码
//在 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 权限
使用命名的参数初始化和默认的构造器
java 复制代码
//定义一个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 复制代码
//在 java 中我们会这样写
try {
    // ...
} catch (Exception e) {
    // do something
}

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

1.5 Groovy 闭包

定义

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

声明

1、闭包基本的语法结构:外面一对大括号,接着是申明参数,参数类型可省略,在是一个 -> 箭头号,最后就是闭包体里面的内容

2、闭包也可以不定义参数,如果闭包没定义参数的话,则隐含有一个参数,这个参数名字叫 it

java 复制代码
//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 进行操作
java 复制代码
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 类,一般我们这种玩家用不到
java 复制代码
//创建一个香蕉类
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关键字

java 复制代码
def groovyArray = ["Java", "Groovy", "Android"] as String[]
列表
java 复制代码
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
java 复制代码
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
java 复制代码
def range = 1..10
//如果不想包含最后一个元素
def range1 = 1..<10

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

2.1 Gradle 构建流程

初始化阶段,配置阶段,执行阶段

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


AAR 和源码的快速切换

java 复制代码
//步骤一:在 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 点

相关推荐
HeDongDong-2 小时前
Kotlin Lambda 表达式详解
android·开发语言·kotlin
行稳方能走远4 小时前
Android C++ 学习笔记3
android·c++
7ioik9 小时前
Explain关键字?
android
海盗12349 小时前
MySQL主从复制的配置方式
android·mysql·adb
liang_jy12 小时前
Android 事件分发机制(二)—— 点击事件透传
android·面试·源码
圆号本昊15 小时前
Flutter Android Live2D 2026 实战:模型加载 + 集成渲染 + 显示全流程 + 10 个核心坑( OpenGL )
android·flutter·live2d
冬奇Lab16 小时前
ANR实战分析:一次audioserver死锁引发的系统级故障排查
android·性能优化·debug