前言
在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也是类似操作)。
简单说明下目录结构里的一些文件的作用及配置用法:
.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的使用方法。