Gradle series (1), understanding of Gradle related concepts, Groovy foundation

foreword

Quoting Gradle’s official introduction to Gradle: Gradle is an open-source build automation tool focused on flexibility and performance. Gradle build scripts are written using a Groovy or Kotlin DSL. The translation is: Gradle is an open-source build automation tool focused on for flexibility and performance. Gradle build scripts are written using the Groovy or Kotlin DSL. The previous introduction on the official website said that Gradle is a Groovy-based DSL. Why is there another Kotlin now? Because Gradle started to support Kotlin DSL from 5.0, and now it has developed to 6.8.3, so we can use Groovy or Kotlin to write Gradle scripts. Kotlin is now the first development language for Android, and its importance is self-evident. As an Android developer, Kotlin is a must-learn, and I will publish a series of Kotlin articles in the future. Today our focus is to introduce some Gradle related concepts and the learning of the Groovy language

1. Problems

I like to be problem-oriented when learning knowledge, which allows me to clarify the purpose of learning and improve learning efficiency. The following are some of the questions I have raised during the process of learning Gradle. We all know that Android applications use Gradle built, when I first developed Android I would think:

1. What is an automated build tool?

2. What is Gradle?

3. What is DSL?

4. What is Groovy?

5. What is the difference between Gradle and Groovy?

6. What is the difference between static programming language and dynamic programming language?

With these questions in mind, we continue to study

1. Automated build tools

The embodiment on Android is simply an automatic compilation and packaging program

When I was studying Java in college, in order to let us deeply experience the charm of coding, the teacher directly typed the code through the text. After typing, the extension was changed to a suffix, and then compiled by command. After the compilation passed, the code was .javaexecuted javac. javacommand to run, then if there are too many files like this, we have to manually operate each time, and the efficiency will be greatly reduced. At this time, an automatic compilation tool appears. We only need to click the compile button in the compilation tool, and after the compilation is completed , the program can run directly without other manual operations, and the automated compilation tool is the earliest automated build tool. Then, with the continuous expansion of business functions, our products need to add multimedia resources, and we need to publish different channel packages to different channels, so we must rely on automated construction tools, which must be able to support differences in platforms, requirements, etc., and be able to add Custom tasks, a program and tool specially used to package and generate the final product, this is the automated construction tool. An automated build tool is essentially a program of code. This is a development process of automated build tools, and automated build tools continue to develop and optimize in this process

2. What is Gradle?

After understanding the automated build tools, it is relatively simple to understand Gradle, or to quote the official passage:

Gradle is an open source build automation tool with a focus on flexibility and performance. Gradle build scripts are written using the Groovy or Kotlin DSL.

Gradle is the default build tool for Android. There are so many things in the Android project, including java, kotlin, C++, and Dart code written by ourselves, as well as the system's own java, C, and C++ code, as well as imported third-party code, and For multimedia resources, so many codes and resources are packaged into APK files, there must be a specification. The one who does this work is the gradle we are familiar with. All in all, Gradle is a tool to help us package APKs.

insert image description here

3. What is DSL?

DSL English full name: domain specific language , Chinese translation is domain specific language, such as: HTML, XML and other DSL languages

features

  • Solve proprietary problems in specific domains
  • It goes to two extremes with the system programming language. The system programming language hopes to solve all problems. For example, the Java language hopes to be able to do Android development, and also hopes to be able to do background development. It has the characteristics of horizontal expansion. And DSL has the characteristic of vertically in-depth solution to specific domain-specific problems.

In general, the core idea of ​​DSL is: "seeking expertise but not perfection, and solving problems in specific fields".

4. What is Groovy?

Groovy is a JVM-based scripting language, which is a dynamic language based on Java extensions

There are many JVM-based languages, such as Groovy, Kotlin, Java, Scala, etc., and they all have a common feature: they will eventually compile and generate Java bytecode files and run them on the JVM.

Because Groovy is an extension of Java, we can learn Groovy the way we learn Java. The learning cost is relatively low. Even if you forget the Groovy syntax during the development process, you can continue coding with Java syntax

5. What is the difference between Gradle and Groovy?

Gradle is an automated build tool based on Groovy. It is a program running on the JVM. Groovy is a language based on the JVM. The relationship between Gradle and Groovy is like the relationship between Android and Java.

6. What is the difference between static programming language and dynamic programming language?

Static programming languages ​​determine the data types of variables at compile time, while dynamic programming languages ​​determine the data types of variables at run time. Just like static proxies and dynamic proxies, one emphasizes the compilation period and the other emphasizes the runtime. Common static programming languages ​​include Java, Kotlin, etc., and dynamic programming languages ​​include Groovy, Python and other languages.

2. Groovy development environment construction and project creation

1. Go to the official website to download JDK installation, and configure the JDK environment

2. Download the Groovy SDK from the official website, and decompress it to a suitable location

3. Configure Groovy environment variables

4. Go to the official website to download the IntelliJ IDEA development tool and install it

5. Create a Groovy project

After completing the above 4 steps, we start to create a Groovy project:

insert image description here

Follow the steps in the above picture to complete the creation of a Groovy project. Next, you can use the tool IntelliJ IDEA to learn Groovy. All the codes below are run on IntelliJ IDEA

Tip: As Android developers, we generally use AndroidStudio for development, but AndroidStudio is not very friendly to Groovy support, there are no prompts, and when it comes to closures, you don’t know the parameters of the closures? So at this time, you can use IntelliJ IDEA to fix it first. After copying the past, IntelliJ IDEA's support for Groovy is still very friendly

3. Groovy Basic Grammar

Emphasize again that Groovy is a dynamic language based on java extensions. It is no problem to write java code directly. In this case, what are the advantages of Groovy?

The reason is that Groovy provides a more flexible and simpler syntax, and a lot of syntactic sugar and closure features allow you to use less code to achieve the same functions as Java. For example, Groovy is very convenient for parsing xml files. It only needs a few lines of code to get it done, but if you use Java, you need dozens of lines of code.

1. Support dynamic type, use def keyword to define a variable

In Groovy, you can use the def keyword to define a variable. Of course, the way of defining data types in Java can be used in Groovy

//Java 中,我们一般会这么定义
int age = 16
String name = "erdai"

//Groovy 中,我们可以这样定义,在变量赋值后,Groovy 编译器会推断出变量的实际类型
def age = 16
def name = 'erdai'

2. Do not write; No.

Now relatively new languages ​​do not need to be written, such as Kotlin

def age = 16
def name = 'erdai'

3. There are no basic data types anymore, all are reference types

As mentioned above, the def keyword is used to define a variable, but Groovy is based on Java extensions, so we can also use types in Java, such as the 8 basic types in Java: byte, short, int, long, float, double, char, boolean

//定义8大基本类型
byte mByte = 1
short mShort = 2
int mInt = 3
long mLong = 4
float mFloat = 5
double mDouble = 6
char mChar = 'a'
boolean mBoolean = true
//对类型进行打印
println(mByte.class)
println(mShort.class)
println(mInt.class)
println(mLong.class)
println(mFloat.class)
println(mDouble.class)
println(mChar.class)
println(mBoolean.class)

//打印结果如下:
class java.lang.Byte
class java.lang.Short
class java.lang.Integer
class java.lang.Long
class java.lang.Float
class java.lang.Double
class java.lang.Character
class java.lang.Boolean

Therefore, we can draw a conclusion: there are no basic data types in Groovy, all are reference types, even if the basic type is defined, it will be converted into the corresponding wrapper class

4. Method changes

1. Use the def keyword to define a method. The method does not need to specify the return value type and parameter type. The last line in the method body will be automatically used as the return value without the return keyword

2. You don’t need to write () in the method call, it’s better to add (), otherwise the readability is not good

3. When defining a method, if the parameter has no return value type, we can omit def and use void

4. In fact, no matter whether there is a return value or not, what is returned in Groovy is Object type

5. The construction method of the class, avoid adding the def keyword

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() {
    
    
      
    }
}

5. String changes

There are three commonly used ways of defining strings in Groovy, as follows:

Here first explain the meaning of the expandable string, the expandable string means that variables, expressions, etc. can be referenced in the string

1. The string defined by single quotes '' is not expandable

2. The string defined by the double quotes "" is an expandable string. In the expandable string, ${} can be used to quote variable values. When there is only one variable in {}, which is not an expression, {} can also be removed.

3. The string defined by triple quotes ''' ''' is an unextensible string with output format

def age = 16
def name = 'erdai'
//定义一个不可扩展字符串,和我门在Java中使用差不多
def str1 = 'hello ' + name
//定义可扩展字符串,字符串里面可以引用变量值,当 {} 里面只有一个变量时,{}也可以去掉
def str2 = "hello $name ${name + age}"
//定义带输出格式的不可扩展字符串 使用 \ 字符来分行
def str3 = '''
\
hello
name
'''
//打印类型和值 下面代码我省略了 println 方法的(),上面有讲到这种语法也是允许的
println 'str1类型: ' + str1.class
println 'str1输出值: ' + str1
println 'str2类型: ' + str2.class
println 'str2输出值: ' + str2
println 'str3类型: ' + str3.class
println 'str3输出值: ' + str3

//打印结果
str1类型: class java.lang.String
str1输出值: hello erdai
str2类型: class org.codehaus.groovy.runtime.GStringImpl
str2输出值: hello erdai erdai16
str3类型: class java.lang.String
str3输出值: 
hello
name

From the above code, we can see that str2 is of GStringImpl type, while str1 and str3 are of String type, so here I have a question, do these two types need to be forced to transfer in the case of mutual assignment? Let's do an experiment under the test:

//定义一个 String 类型的变量接收 GStringImpl 类型的变量,并没有强转
String str4 = str2
println 'str4类型: ' + str4.class
println 'str4输出值: ' + str4

//打印类型和值
str4类型: class java.lang.String
str4输出值: hello erdai erdai16

So we can draw a conclusion: In the process of coding, we don't need to pay special attention to the difference between String and GString, the compiler will help us to convert the type automatically .

6. No need to write get and set methods

1. When we create properties, Groovy will automatically create get and set methods for us

2. When we only define the get method of an attribute but do not define the attribute, the attribute is read-only by default

3. When we use the object object.field to get a value or use object.field = value to assign a value, it will actually automatically call the object.getField() and object.setField(value) methods. If we don’t want to When calling this special get method, you can use the .@ direct field access operator to access the property itself

Let's simulate the three situations 1, 2, and 3

//情况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 则会访问这个属性本身

7. Class is a first-class citizen, all Class types can be omitted. Class

//定义一个Test类
class Test{
    
     

}

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

//测试
testClass(Test.class)
testClass(Test)

8. == and equals

In Groovy, == is equivalent to Java's equals. If you need to compare whether two objects are the same, you need to use .is()

class People{
    
    
    def name
    def age
}

def people1 = new People(name: 'erdai666')
def people2 = new People(name: 'erdai666')

println("people1.name == people2.name is: " + (people1.name == people2.name))
println("people1 is people2 is: " + people1.is(people2))

//打印结果
people1.name == people2.name is: true
people1 is people2 is: false

9. Use assert to set the assertion. When the condition of the assertion is false, the program will throw an exception

assert  2 ** 4 == 15
//运行程序,报错了,结果如下:
Caught: Assertion failed: 
assert  2 ** 4 == 15
          |    |
          16   false

10. Support ** power operator

assert  2 ** 4 == 16

11. Concise ternary expressions

//在java中,我们会这么写
String str = obj != null ? obj : ""

//在Groovy中,我们可以这样写,?: 操作符表示如果左边结果不为空则取左边的值,否则取右边的值
String str = obj ?: ""

12. Concise non-null judgment

//在java中,我们可能会这么写
if(obj != null){
    
    
    if(obj.group != null){
    
    
        if(obj.group.artifact != null){
    
    
            //do something
        }
    }
}

//在Groovy中,我们可以这样写 ?. 操作符表示如果当前调用对象为空就不执行了
obj?.group?.artifact


13. Powerful Switch

In Groovy, the switch method becomes more flexible and powerful, and can support more parameter types at the same time, which is much enhanced than in Java

def result = 'erdai666'
switch (result){
    
    
    case [1,2,'erdai666']:
        println "匹配到了result"
        break
    default:
        println 'default'
        break
}
//打印结果
匹配到了result

14. Determine whether it is null and not operator

In Groovy, all types can be converted into Boolean values, such as null is equivalent to 0 or equivalent false, and others are equivalent totrue

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

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

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

15. You can use the Number class to replace float, double and other types, saving the trouble of considering precision

16. The default is public permission

By default, Groovy classes and methods have public permissions, so we can omit the public keyword unless we want to use the private modifier

class Server {
    
     
   String toString() {
    
     "a server" }
}

17. Use named parameter initialization and default constructors

In Groovy, when we create an object instance, we can directly assign values ​​to attributes in the form of key value in the construction method, without writing the construction method, which is a bit abstract. Let’s feel it in the code:

//定义一个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')

18. Use the with function to operate multiple properties and methods of the same object

The with function receives a closure, which will be discussed below, and the parameter of the closure is the object currently called

class People{
    
    
    def name
    def age
    
    void running(){
    
    
        println '跑步'
    }
}
//定义一个 people 对象
def people = new People()
//调用 with 函数 闭包参数即为peopeo 如果闭包不写参数,默认会有一个 it 参数
people.with{
    
    
    name = "erdai"
    age = 19
    println "$name $age"
    running()
}
//打印结果
erdai 19
跑步

19. Exception capture

If you really don't want to care about what kind of exception is thrown in the try block, you can simply catch all exceptions, and you can omit the exception type:

//在 java 中我们会这样写
try {
    
    
    // ...
} catch (Exception e) {
    
    
    // do something
}

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


The above Groovy writing method actually omits the parameter type. In fact, the parameter type of any is also Exception, which does not include Throwable. If you want to catch all exceptions, you can explicitly capture the parameter type of the exception

4. Groovy closures

In Groovy, closures are very important, so use a module alone

1. Closure definition

Quoting Groovy's official definition of closure: A closure in Groovy is an open, anonymous, block of code that can take arguments, return a value and be assigned to a variable. The translation is: a closure in Groovy is an open , an anonymous block of code that can accept parameters, return values, and assign values ​​to variables. In layman's terms, a closure can be used as a method parameter and return value, or as a variable. A closure is essentially a block of code. Let's learn about closures from the shallower to the deeper

2. Closure statement

1. The basic grammatical structure of the closure: a pair of curly braces outside, followed by the declaration parameter, the parameter type can be omitted, there is a -> arrow number, and finally the content inside the closure body

2. The closure can also not define parameters. If the closure does not define parameters, there is an implicit parameter. The name of this parameter is it

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

//2
{
    
     
   //do something
}

3. Closure call

1. The closure can be called by the .call method

2. Closures can be called directly with parentheses + parameters

//定义一个闭包赋值给 closure 变量
def closure = {
    
     params1,params2 ->
    params1 + params2
}

//闭包调用方式1: 闭包可以通过 .call 方法来调用
def result1 = closure('erdai ','666')
//闭包调用方式2: 闭包可以直接用括号+参数来调用
def result2 = closure.call('erdai ','777')
//打印值
println result1
println result2
//打印结果
erdai 666
erdai 777

//定义一个无参闭包
def closure1 = {
    
    
    println('无定义参数闭包')
}
closure1() //或者调用 closure1.call()
//打印结果
无定义参数闭包

4. Advanced closure

1), the key variable in the closure

Each closure contains three built-in objects , this, owner and delegate , so what is the difference between these three built-in objects? Let's verify it with code

Note :

1. The getThisObject() method and thisObject property are equivalent to this

2. The getOwner() method is equivalent to owner

3. The getDelegate() method is equivalent to delegate

If you look at the source code of the closure, you will have a deep understanding

1. We define a closure in the GroovyGrammar.groovy script class to print the values ​​of the three. Take a look:

//定义一个闭包
def outerClosure = {
    
    
    println "this: " + this
    println "owner: " + owner
    println "delegate: " + delegate
}
//调用闭包
outerClosure.call()
//打印结果
this: variable.GroovyGrammar@39dcf4b0
owner: variable.GroovyGrammar@39dcf4b0
delegate: variable.GroovyGrammar@39dcf4b0
//证明当前三者都指向了GroovyGrammar这个脚本类对象

2. We define a class in the GroovyGrammar.groovy script class, define a closure in the class, and print to see the result:

//定义一个 OuterClass 类
class OuterClass {
    
    
    //定义一个闭包
    def outerClosure = {
    
    
        println "this: " + this
        println "owner: " + owner
        println "delegate: " + delegate
    }
}

def outerClass = new OuterClass()
outerClass.outerClosure.call()

//打印结果如下:
this: variable.OuterClass@1992eaf4
owner: variable.OuterClass@1992eaf4
delegate: variable.OuterClass@1992eaf4
//结果证明这三者都指向了当前 OuterClass 类对象

3. We define a closure in the GroovyGrammar.groovy script class, and define a closure in the closure, and print the results:

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 闭包对象

Let's sort out the above three situations:

1. The closure is defined in the GroovyGrammar.groovy script class, and this owner delegate points to the script class object

2. I created an OuterClass class in this script class, and defined a closure in it, then this owner delegate points to the OuterClass class object at this time

3. I defined a closure in the GroovyGrammar.groovy script class, and another closure was defined in the closure. this points to the current GroovyGrammar script class object, and both owner and delegate point to the outerClosure closure object

Therefore we can conclude:

1. this always points to the closest class object that defines the closure. The principle of proximity, when defining a closure, whichever class is closest to it points to which one. The closest here refers to the class that defines the closure, including internal classes

2. The owner always points to the class object or closure object that defines the closure. As the name implies, a closure can only be defined in a class or closure

3. The delegate and the owner are the same. We can see in the source code of the closure that the owner will assign its own value to the delegate, but at the same time the delegate can also assign other values

Note: When we use this, owner, and delegate, this and owner are read-only by default, and we cannot modify them externally. This is also reflected in the source code, but we can operate on the delegate

2) Closure delegation strategy

Next, let's practice modifying the delegate of the 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 对象

Let's analyze the cause of the error. Before the analysis, we need to understand a knowledge point:

The default delegation strategy of the closure is OWNER_FIRST, that is, the closure will first look for the property or method from the owner, and if it cannot find it, it will look for it on the delegate

1. closure This closure is born in the GroovyGrammar script class

2. According to the knowledge we have learned before, the delegate and the owner are the same without changing the delegate, and both will point to the GroovyGrammar script class object

3. The GroovyGrammar script class object, according to the closure default delegation strategy, cannot find the name attribute

Therefore, an error is reported, and we know the reason for the error, then we modify the delegate of the closure, or the above code, and add the following code:

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

At this time, the delegate of the closure points to orange, so the name of the orange object will be printed, then we change the delegate of the closure to banana, and bananas will definitely be printed

//修改闭包的delegate
closure.delegate = banana
//我们在运行一下,打印结果:
香蕉

3) In-depth closure delegation strategy

//定义一个 ClosureDepth 类
class ClosureDepth{
    
    
    //定义一个变量 str1 赋值为 erdai666
    def str1 = 'erdai666'
    //定义一个闭包
    def outerClosure = {
    
    
      	//定义一个变量 str2 赋值为 erdai777
        def str2 = 'erdai777'
      	//打印str1 分析1
        println str1
				
      	//闭包中在定义一个闭包
        def innerClosure = {
    
    
            //分析2
            println str1
            println str2
        }
        //调用内部这个闭包
        innerClosure.call()
    }
}

//创建 ClosureDepth 对象
def closureDepth = new ClosureDepth()
//调用外部闭包
closureDepth.outerClosure.call()
//运行程序,打印结果如下
erdai666
erdai666
erdai777

The above code comments are written very clearly, now let’s focus on analyzing the printed values ​​at Analysis 1 and Analysis 2:

Analysis 1:

Analysis 1 prints str1, which is in the closure of outerClosure. At this time, the owner and delegate of the closure of outerClosure point to the class object of ClosureDepth, so we can call the properties and methods of the class object of ClosureDepth, so Analysis 1 will print erdai666

Analysis 2:

Analysis 2 prints str1 and str2, which are in the innerClosure closure. At this time, the owner and delegate of the innerClosure closure will point to the outerClosure closure object, and we will find that the outerClosure has the str2 attribute, but does not have the str1 attribute. , so the closure of outerClosure will search for its owner, so it will find the str1 property of the ClosureDepth class object, so the printed str1 is the property of the ClosureDepth class object, and the printed str2 is the property of the outerClosure closure. So the print results of analysis 2 are erdai666 erdai777

In the above example, no receiver is explicitly set for the delegate, but no matter which layer of closure can successfully access the values ​​of str1 and str2, this is because the default parsing delegation strategy is in effect, and the delegation strategy of the Groovy closure has The following types:

  1. OWNER_FIRST : The default strategy, first look for the property or method from the owner, if not found, look for it on the delegate
  2. DELEGATE_FIRST : Contrary to the above, first look for attributes or methods from the delegate, if not found, look for the owner
  3. OWNER_ONLY : look only on owner, delegate is ignored
  4. DELEGATE_ONLY : Contrary to the above, it is only found on the delegate, and the owner is ignored
  5. TO_SELF : Advanced options, allowing developers to customize strategies, must implement a Closure class, generally not used by players like us

Next, let's modify the delegation strategy of the closure to deepen our understanding:

class People1{
    
    
    def name = '我是People1'

    def action(){
    
    
        println '吃饭'
    }

    def closure = {
    
    
        println name
        action()
    }
}

class People2{
    
    
    def name = '我是People2'

    def action(){
    
    
        println '睡觉'
    }
}

def people1 = new People1()
def people2 = new People2()
people1.closure.delegate = people2
people1.closure.call()
//运行下程序,打印结果如下:
我是People1
吃饭

what? What is the situation? I changed the delegate to people2, so why is the print result still people1? That's because we overlooked a point and didn't modify the closure delegation strategy. It defaults to OWNER_FIRST, so we can modify it. Or the above code, add a sentence of code as follows:

people1.closure.resolveStrategy = Closure.DELEGATE_FIRST
//运行下程序,打印结果如下:
我是People2
睡觉

At this point, I believe you have a good understanding of closures. Next, we will look at the source code of closures and it will be perfect.

4) Closure class source code

Only post the key source code

public abstract class Closure<V> extends GroovyObjectSupport implements Cloneable, Runnable, GroovyCallable<V>, Serializable {
    
    
    /**
     * 熟悉的一堆闭包委托代理策略
     */
    public static final int OWNER_FIRST = 0;
    public static final int DELEGATE_FIRST = 1;
    public static final int OWNER_ONLY = 2;
    public static final int DELEGATE_ONLY = 3;
    public static final int TO_SELF = 4;
    /**
     * 闭包对应的三个委托对象 thisObject 对应的就是 this 属性,都是用 private 修饰的,外界访问不到
     */
    private Object delegate;
    private Object owner;
    private Object thisObject;
    /**
     * 闭包委托策略
     */
    private int resolveStrategy;
		
    /**
     * 在闭包的构造方法中:
     * 1、将 resolveStrategy 赋值为0,也是就默认委托策略OWNER_FIRST
     * 2、thisObject ,owner ,delegate都会被赋值,delegate 赋的是 owner的值
     */
    public Closure(Object owner, Object thisObject) {
    
    
        this.resolveStrategy = 0;
        this.owner = owner;
        this.delegate = owner;
        this.thisObject = thisObject;
        CachedClosureClass cachedClass = (CachedClosureClass)ReflectionCache.getCachedClass(this.getClass());
        this.parameterTypes = cachedClass.getParameterTypes();
        this.maximumNumberOfParameters = cachedClass.getMaximumNumberOfParameters();
    }
  
    /**
     * thisObject 只提供了 get 方法,且 thisObject 是用 private 修饰的,因此 thisObject 即 this 只读
     */
  	public Object getThisObject() {
    
    
        return this.thisObject;
    }
  
    /**
     * owner 只提供了 get 方法,且 owner 是用 private 修饰的,因此 owner 只读
     */
  	public Object getOwner() {
    
    
        return this.owner;
    }

    /**
     * delegate 提供了 get 和 set 方法,因此 delegate 可读写
     */
    public Object getDelegate() {
    
    
        return this.delegate;
    }

    public void setDelegate(Object delegate) {
    
    
        this.delegate = delegate;
    }
  
    /**
     * 熟悉的委托策略设置
     */
    public void setResolveStrategy(int resolveStrategy) {
    
    
        this.resolveStrategy = resolveStrategy;
    }
    public int getResolveStrategy() {
    
    
        return resolveStrategy;
    }
}

So far, we have finished all the knowledge points related to closures, but, but, but, the important thing is said three times: when we use closures, how to determine the parameters of closures? , this is really painful. As an Android developer, when using AndroidStudio to write Gradle scripts, it is really very unfriendly. I mentioned above that you can use a small trick to solve this problem, but this situation is in When you know that you want to use an Api, for example, you know that the each method of Map can be traversed, but you don't know the parameters, you can use it at this time. If you don't even know the Api, you don't even know the parameters of the closure, so to solve this situation, we have to consult the official Groovy documentation:

www.groovy-lang.org/api.html

docs.groovy-lang.org/latest/html…

Five, Groovy data structure

Through the study of this module, I will combine specific examples to illustrate how to consult the documentation to determine the parameters in the closure. When talking about Map, I will talk about

There are four commonly used data structures in Groovy:

  • 1), array
  • 2)、List
  • 3)、Map
  • 4)、Range

1. Array

Using [ ] in Groovy represents a List collection. If we want to define an Array array, we must force it to be an array type

//在 Java 中,我们一般会这样去定义一个数组
String[] javaArray = ["Java", "Groovy", "Android"]

//在 Groovy 中,我们一般会使用 as 关键字定义数组
def groovyArray = ["Java", "Groovy", "Android"] as String[]

2、List

1), list set definition

1. List is a collection of lists, corresponding to the List interface in Java, and ArrayList is generally used as the real implementation class

2. The way to define a list collection is a bit like defining an array in Java

3. Collection elements can receive any data type

//在 Groovy 中定义的集合默认就是对应于 Java 中 ArrayList 集合
def list1 = [1,2,3]
//打印 list 类型
print list1.class
//打印结果
class java.util.ArrayList

//集合元素可以接收任意的数据类型
def list2 = ['erdai666', 1, true]

So here comes the question, if I want to define a LinkedList collection, how should I do it? There are two ways:

1. Defined by Java's strong type

2. Specify by as keyword

//方式1:通过 Java 的强类型方式去定义
LinkedList list3 = [4, 5, 6]

//方式2:通过 as 关键字来指定
def list4 = [1, 2, 3] as LinkedList

2) Addition, deletion, modification and query of list collection

def list = [1,2,3]
//-------------------------- 增加元素 ---------------------------------
//有以下几种方式
list.add(20)
list.leftShift(20)
list << 20

//-------------------------- 删除元素 ---------------------------------
//根据下标移除元素
list.remove(0)

//-------------------------- 修改元素 ---------------------------------
//根据下标修改元素
list[0] = 100

//-------------------------- 查询元素 ---------------------------------
//调用闭包的 find 方法,方法中接收一个闭包,闭包的参数就是 list 中的元素
list.find {
    
    
    println it
}

There are quite a lot of list collection APIs. For some other APIs, you can consult the documentation yourself when you use it. I will demonstrate the reference to the API documentation to determine the parameters of the closure when I talk about Map below.

3、Map

1), definition

1. Map represents a key-value table, and its underlying layer corresponds to LinkedHashMap in Java

2. Map variables are defined by [:], the left side of the colon is the key, and the right side is the Value. key must be a string, value can be any object

3. The key of Map can be wrapped with '' or "" or ''' ''', or without quotation marks

def map = [a: 1, 'b': true, "c" : "Groovy", '''d''' : '''ddd''']

2) Common operations of Map

Here are some common operations of Map. When using some other APIs, you can consult the documentation by yourself.

//---------------------------- Map 中元素访问操作 ----------------
/**
 * 有如下三种方式:
 * 1、map.key
 * 2、map[key]
 * 3、map.get(ket)
 */
println map.a
println map['b']
println map.get('c')
//打印结果
1
true
Groovy

//---------------------------- Map 中添加和修改元素 -------------------
//如果当前 key 在 map 中不存在,则添加该元素,如果存在则修改该元素
map.put('key','value')
map['key'] = "value"

3), Map traversal, demo refer to official documents

Now I'm going to traverse the elements in the map, but I don't know what its Api is, so I have to check the official Api documentation at this time:

docs.groovy-lang.org/latest/html…

insert image description here

We found that Map has two APIs, each and eachWithIndex, which can perform traversal operations, and then click in to take a look

insert image description here

insert image description here

Through the official documentation, we can find that the closure parameters of each and eachWithIndex are still uncertain. If we use the each method, if it is a parameter passed to the closure, then it will take the entry as a parameter. If we pass it to the closure is two parameters, then it takes key and value as parameters, and eachWithIndex has one more index subscript than each.

So we now use the following two APIs:

//下面为了打印输出的格式清晰,做了一些额外的操作
def map = [a: 1, 'b': true, "c" : "Groovy", '''d''' : '''ddd''']

map.each {
    
    
    print "$it.key $it.value \t"
}
println()

map.each {
    
    key,value ->
    print "$key $value \t"
}
println()

map.eachWithIndex {
    
    entry,index ->
    print "$entry.key $entry.value $index \t"
}
println()

map.eachWithIndex {
    
     key,value,index ->
    print "$key $value $index \t"
}
//打印结果
a 1 	b true 	c Groovy 	d ddd 	
a 1 	b true 	c Groovy 	d ddd 	
a 1 0 	b true 1 	c Groovy 2 	d ddd 3 	
a 1 0 	b true 1 	c Groovy 2 	d ddd 3 

4、Range

Range represents the range, which is actually an extension of List. It is represented by begin value + two dots + end value. If you don't want to include the last element, begin value + two dots + < + end means. We can obtain the corresponding boundary elements through aRange.from and aRange.to , and experience it in practice:

//定义一个两端都是闭区间的范围
def range = 1..10
range.each {
    
    
    print it + " "
}
//打印值
1 2 3 4 5 6 7 8 9 10

//如果不想包含最后一个元素
def range1 = 1..<10
range1.each {
    
    
    print it + " "
}
//打印结果
1 2 3 4 5 6 7 8 9

//打印头尾边界元素
println "$range1.from $range1.to"
//打印结果
1 9

6. Groovy file processing

1、IO

For some Api operations commonly used in IO, let’s go directly to the code to see the effect, and the code will write clear comments:

Preparations: I create a testFile.txt file in the same directory as the current script file, and write some strings in it, as shown in the figure below:

insert image description here

Let's start to operate this file. For the readability of the closure, I will add the type and parameters to the closure:

//-------------------------------1、文件定位 --------------------------------
def file = new File('testFile.txt')

//-----------------------2、使用 eachLine Api 每次读取一行, 闭包参数是每一行的字符串------------
file.eachLine {
    
     String line ->
    println line
}
//打印结果
erdai666
erdai777
erdai888

//------------------------3、获取输入流,输出流读文件和写文件---------------------------------
//获取输入流读取文件的每一行
//1
file.withInputStream {
    
     InputStream inputStream ->
    inputStream.eachLine {
    
     String it ->
        println it
    }
}

//2
file.withReader {
    
     BufferedReader it ->
    it.readLines().each {
    
     String it ->
        println it
    }
}

//打印结果
erdai666
erdai777
erdai888

//获取输出流将字符串写入文件 下面这两种方式写入的文件内容会把之前的内容给覆盖
//1
file.withOutputStream {
    
     OutputStream outputStream ->
    outputStream.write("erdai999".getBytes())
}

//2
file.withWriter {
    
     BufferedWriter it ->
    it.write('erdai999')
}

//------------------------4、通过输入输出流实现文件拷贝功能---------------------------------
//1、通过 withOutputStream withInputStream 实现文件拷贝
def targetFile = new File('testFile1.txt')
targetFile.withOutputStream {
    
     OutputStream outputStream ->
    file.withInputStream {
    
     InputStream inputStream ->
        outputStream << inputStream
    }
}

//2、通过 withReader、withWriter 实现文件拷贝
targetFile.withWriter {
    
    BufferedWriter bufferedWriter ->
    file.withReader {
    
    BufferedReader bufferedReader ->
        bufferedReader.eachLine {
    
    String line ->
            bufferedWriter.write(line + "\r\n")
        }
    }
}

2. XML file operation

1), parse the XML file

//定义一个带格式的 xml 字符串
def xml = '''
    <response>
        <value>
            <books id="1" classification="android">
                <book available="14" id="2">
                   <title>第一行代码</title>
                   <author id="2">郭霖</author>
               </book>
               <book available="13" id="3">
                   <title>Android开发艺术探索</title>
                   <author id="3">任玉刚</author>
               </book>
           </books>
       </value>
    </response>
'''
//创建 XmlSlurper 类对象,解析 XML 文件主要借助 XmlSlurper 这个类
def xmlSlurper = new XmlSlurper()
//解析 mxl 返回 response 根结点对象
def response = xmlSlurper.parseText(xml)
//打印一些结果
println response.value.books[0].book[0].title.text()
println response.value.books[0].book[0].author.text()
//打印结果
第一行代码
郭霖

//1、使用迭代器解析
response.value.books.each{
    
     books ->
    books.book.each{
    
     book ->
        println book.title
        println book.author 
    }
}
//打印结果
第一行代码
郭霖
Android开发艺术探索
任玉刚

//2、深度遍历 XML 数据
def str1 = response.depthFirst().findAll {
    
     book ->
    return book.author == '郭霖'
}
println str1
//打印结果
[第一行代码郭霖]

//3、广度遍历 XML 数据
def str2 = response.value.books.children().findAll{
    
     node ->
    node.name() == 'book' && node.@id == '2'
}.collect {
    
     node ->
    "$node.title $node.author"
}
println str2
//打印结果
[第一行代码 郭霖]

2), generate XML files

Above we use the XmlSlurper class to parse XML, now we use MarkupBuilder to generate XML, the code is as follows:

/**
 * <response>
 *      <value>
 *          <books id="1" classification="android">
 *              <book available="14" id="2">
 *                 <title>第一行代码</title>
 *                 <author id="2">郭霖</author>
 *             </book>
 *             <book available="13" id="3">
 *                 <title>Android开发艺术探索</title>
 *                 <author id="3">任玉刚</author>
 *             </book>
 *         </books>
 *     </value>
 * </response>
 */
//方式1:通过下面这种方式 就可以实现上面的效果,但是这种方式有个弊端,数据都是写死的
def sw = new StringWriter()
def xmlBuilder = new MarkupBuilder(sw)
xmlBuilder.response{
    
    
    value{
    
    
        books(id: '1',classification: 'android'){
    
    
            book(available: '14',id: '2'){
    
    
                title('第一行代码')
                author(id: '2' ,'郭霖')
            }
            book(available: '13',id: '3'){
    
    
                title('Android开发艺术探索')
                author(id: '3' ,'任玉刚')
            }
        }
    }
}
println sw

//方式2:将 XML 数据对应创建相应的数据模型,就像我们解析 Json 创建相应的数据模型是一样的
//创建 XML 对应数据模型
class Response {
    
    
    
    def value = new Value()

    class Value {
    
    

        def books = new Books(id: '1', classification: 'android')

        class Books {
    
    
            def id
            def classification
            def book = [new Book(available: '14', id: '2', title: '第一行代码', authorId: 2, author: '郭霖'),
               new Book(available: '13', id: '3', title: 'Android开发艺术探索', authorId: 3, author: '任玉刚')]

            class Book {
    
    
                def available
                def id
                def title
                def authorId
                def author
            }
        }
    }
}

//创建 response 对象
def response = new Response()
//构建 XML 
xmlBuilder.response{
    
    
    value{
    
    
        books(id: response.value.books.id,classification: response.value.books.classification){
    
    
            response.value.books.book.each{
    
    
                def book1 = it
                book(available: it.available,id: it.id){
    
    
                    title(book1.title)
                    author(authorId: book1.authorId,book1.author)
                }
            }
        }
    }
}
println sw

3. Json analysis

Json parsing is mainly implemented through the JsonSlurper class, so we don’t need to introduce a third-party Json parsing library when writing plug-ins. The sample code is as follows:

//发送请求获取服务器响应的数据
def response = getNetWorkData("https://www.wanandroid.com/banner/json")
println response.data[0].desc
println response.data[0].imagePath

def getNetWorkData(String url){
    
    
    def connect = new URL(url).openConnection()
    connect.setRequestMethod("GET")
    //这个会阻塞线程 在Android中不能这样操作 但是在桌面程序是可以的
    connect.connect()
    def response = connect.content.text

    //json转实体对象
    def jsonSlurper = new JsonSlurper()
    jsonSlurper.parseText(response)
}
//打印结果
扔物线
https://wanandroid.com/blogimgs/8a0131ac-05b7-4b6c-a8d0-f438678834ba.png

7. Summary

In this article, we mainly introduced the following parts:

1. Some questions about Gradle and Groovy

2. Build a Groovy development environment and create a Groovy project

3. Explain some basic syntax of Groovy

4. In-depth explanation of closure

5. Explain the data structure in Groovy and the use of commonly used Api, and take Map as an example, consult the official documents to determine the use of Api and the parameters of the closure

6. Explained the processing of Groovy files

Learning Groovy is a key step for our subsequent custom Gradle plugin. Secondly, if you have studied Kotlin, you will find that their syntax is very similar, so we can quickly get started with subsequent learning of Kotlin.

reference and recommendation

In-depth exploration of Gradle automation construction technology (2. Groovy Foundation Building)

Gradle Crawling Guide--First Concept, Grovvy Syntax, Common API

A preliminary exploration of the essence of Groovy and a summary of the principle of closure characteristics

Gradle from entry to actual combat - Groovy basics

Gradle 3.0 Automation Project Construction Technology Intensive Lecture + Practical Combat of MOOC

Reprint: https://juejin.cn/post/6939662617224937503

Guess you like

Origin blog.csdn.net/gqg_guan/article/details/132481019