给前端开发的Gradle介绍

前言

在RN(ReactNative)项目的开发中,每次遇到编译错误就很头疼(血压爆升)。于是痛定思痛,决定去学习原生开发的一些知识。现在将学习到的一些内容分享给大家,希望能帮助到有需要的人。
在安卓端,是由今天的主角gradle去负责项目的编译构建。观看前最好准备Android Studio或者IntelliJ IDEA。

gradle简介

gradle是一个自动化构建工具,构建脚本使用groovy编写。为了快速理解,我们可以把gradle对应成前端的webpack,而groovy则是对应nodeJS。然后我们来看两者的作用,便于加深理解。

在前端开发中,我们想运行JS代码,则需要把代码插入到HTML的

在java开发里,想要运行java代码,则需要把.java源代码文件编译成.class字节码文件,然后就可以在JVM上运行了。我们RN项目在安卓端会打包出apk文件,apk文件其实就是一个压缩包,里面会包含dex文件,而dex文件可以简单理解成多个class文件的组合体。所以我们安卓端的构建可以理解成把java代码编译成class文件,然后整合成dex文件,跟其他资源文件一起被压缩成apk文件。这些任务就是由gradle负责的。

总之就是把gradle理解成webpack,作用就是构建项目。

groovy语言

gradle构建脚本可以用groovy或者kotlin编写,目前主流还是用groovy,react-native-cli脚手架生成的项目模板也是用的groovy(.gradle后缀的文件就是用的groovy语言)。所以还是学groovy方便。

groovy是完全兼容java代码的,不过项目中不太会直接用java去写。下面分享一些常用的语法,并给出JS代码的对照。

※声明变量:使用def声明。如:

def a = '1'
def int b = 2 // 带类型
复制代码

类似TS的

let a = '1'
let b: number = 2 // 带类型
复制代码

※集合:

def arr = [1,2,3]
println arr[0] // 1
def obj = [a:1]
println obj.a // 1
复制代码

类似JS的

let arr = [1,2,3]
console.log(arr[0]) // 1
let obj = {a:1}
console.log(obj.a) // 1
复制代码

※字符串:双引号里面用美元符使用变量,如

def a = 2
print "age=$a" // 输出age=2。
复制代码

类似JS的

let a = 2
console.log(`age=${a}`) // 输出age=2。
复制代码

单引号则没有这个功能,跟JS的单引号一样,就是个字符串。

※方法:

def method() { 
    print 'method'
}
method()
// 输出method
复制代码

类似JS的

function method() { 
    console.log('method')
} 
method()
// 输出method
复制代码

※闭包:当方法返回一个代码块的时候就是一个闭包。

def closure() { 
    return { 
        print 'closure'
    }
}

//可以简写成
def closure = { 
    print 'closure' 
}

//如果需要传参可以使用it,如
def closure = { it ->
    print it
}
closure 1 
//输出1
复制代码

在groovy里用闭包用的最多的是当成函数的参数。如

def method(n, closure) { 
    print n
    closure();
}
method(1, {
    print 'hello'
})
// 输出hello

// 如果闭包是函数的最后一个参数,则可以把大括号写在外面。如:
method(1) { 
    print 'hello'
}
复制代码

上面的案例为了让大家习惯不带括号的用法,groovy函数调用都尽量去掉了括号。
在groovy里函数调用是可以不带括号的。 比如

id 'com.android.application'
// 等于
id('com.android.application')

plugin { 
    id 'com.android.application'
}
// 等于
plugin({ 
    id('com.android.application') 
})
复制代码

这种省略括号的用法在gradle构建脚本里是很常见的。

除了省略括号,groovy还可以省略分号、return、def、Map的方括号、it、类型等(作者是有多懒)。

做个groovy小练习,需求是将字符串数组中的x.gradle改成x.gradle.kts。(找到Android Studio或者 IntelliJ IDEA的Tools -> Groovy Console,点击后可以运行groovy代码)

我们一般的写法是先遍历数组,然后把元素中以.gradle结尾的字符增加一个.kts。那么直接翻译成groovy:

arr = ['a.gradle', 'b.2.gradle', 'c', '.d', 'gradle.e']
addSuffix = { array, suffix ->
    for (i = 0; i < array.size(); i++) {
        array[i] = array[i].replaceFirst(/(\.gradle)$/, '$1.' + suffix)
    }
}
addSuffix arr, 'kts'
print arr
// 结果输出[a.gradle.kts, b.2.gradle.kts, c, .d, gradle.e]
复制代码

习惯groovy的各种省略,还有闭包的使用方法,基本就能看懂大部分gradle脚本了。

gradle构建介绍

打开Android Studio,选择File->New->New Project,点击Empty Activity,就可以看到截图的项目结构(IDEA也是类似操作)。

gradle-image.png

简单说明下目录结构里的一些文件的作用及配置用法:

.properties文件就类似.json文件,数据表达能力没有json丰富,但是可以写注释。
形式:key=value

app目录 -> build.gradle文件,声明项目的配置

plugins {
    id 'com.android.application'//gradle插件,调用之后就可以使用android{}设置安卓项目打包的配置
}
android {// app构建的参数
    defaultConfig {
         versionCode 1  
    }
}
dependencies { // 项目构建依赖的模块
    implementation 'androidx.appcompat:appcompat:1.2.0'
}
复制代码

gradle目录 -> wrapper目录 -> gradle-wrapper.properties文件,说明gradle的版本

distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
复制代码

根目录 -> build.gradle文件,声明所有模块的构建配置

buildscript {// gradle脚本自身需要使用的资源
    dependencies {// 类似前端项目package.json里的devDependencies
        classpath 'com.android.tools.build:gradle:4.1.0'// gradle插件,调用gradle跟Android SDK的功能
    }
}
allprojects {// 所有project的配置
    repositories {// 插件依赖的仓库
        google()
        jcenter()
    }
}
复制代码

根目录 -> gradlew/gradlew.bat文件,用来启动编译命令。在mac系统就是用gradlew,Windows就是用gradle.bat。Android Studio右侧的gradle栏会列出task任务,我们双击里面的task,就等于执行./gradlew xx命令。

task helloTask {
    doLast {
        print 'hello'
    }
}
// 在.gradle文件里执行后再Android Studio右侧task列表就会增加一个helloTask任务
复制代码

根目录 -> settings.gradle文件,声明哪些模块要参与构建

include ‘:app’ // 表示该project要参与构建
复制代码

大概了解了配置文件的作用之后,现在简单说下gradle的执行流程。

gradle分为三个阶段,初始化阶段、配置阶段跟执行阶段。初始化阶段就是执行settings.gradle的代码,声明哪些模块参与构建。配置阶段就是执行所有项目的build.gradle代码,增加配置。最后是执行阶段就是执行对应的task。
gradle也提供了对应生命周期的钩子函数:

gradle.settingsEvaluated {
    println "settings.gradle执行后调用"
}
gradle.buildFinished {
    print 'task执行完毕'
}
复制代码

还有很多钩子函数就不一一列举了,感兴趣的可以看这篇文章www.jianshu.com/p/2e19268bf…

这里顺便补充一点task的内容:
task 是 Gradle 里项目构建的原子执行单元,我们执行的打包命令(如assembleDebug)是由多个task组成的。
注册task的时候,doFirst跟doLast里面的代码不会立即执行,而是在调用task的时候,先执行doFirst再执行doLast。
dependsOn就是表示当前task依赖某一个task,例如下面的代码,执行hiTask会先去执行helloTask。

task helloTask {
    doLast {
        println 'hello'
    }
}

task hiTask {
    dependsOn helloTask //执行hiTask会首先执行helloTask
    doLast {
        println 'depends hello'
    }
}
复制代码

然后我们完整描述一下执行流程:在命令行执行./gradlew assembleRelease后,会根据gradle-wrapper.properties文件去下载对应版本的gradle,然后根据settings.gradle文件指定哪些模块参与构建;接下来执行根目录的build.gradle文件,再执行各个模块的build.gradle文件;执行完毕之后就执行名字为assembleRelease的task任务。

最后:

学习gradle感觉就是要把里面的.gradle文件当成代码去理解,不要理解成配置文件。
对RN开发来说,大多时候只需要看懂代码、理解作用就行。
如果有深度使用的需求,可以去看gradle跟gradle插件的源码,或者是去手写一些简单的gradle插件,这样能快速掌握gradle的使用方法。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享