持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
Gradle
Gradle 是一个构建工具,是一个高级构建工具包。管理依赖并可以自定义构建流程
优势
- 代码和资源易于重用
- 不管是针对多个APK发行版还是多种不同风格和产品定位的应用程序,都可以很容易创建应用程序的多个不同版本
- 易于配扩展和自定义构建过程
- 良好的IDE集成
Android的Gradle目录层级和作用
- 项目层级下的build.gradle :配置项目的整体属性、像代码仓库、依赖的gradle插件版本
- 模块build.gradle 配置当前module的编译参数
- gradle-wrapper.properties 配置Gradle wrapper
- gradle.properties 配置Gradle编译参数
- settings.gradle gradle的多项目管理
- local.properties 该项目的私有属性配置、如SDK位置
gradle 方法
长得像配置的方法调用 以下的方式都可以
classpath ('com.android.tools.build:gradle:4.2.1')
classpath 'com.android.tools.build:gradle:4.2.1'
println("abc")
println "abc"
我们也可以见到以下这样的形式
dependencies ({
classpath ('com.android.tools.build:gradle:4.2.1')
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
})
dependencies {
classpath ('com.android.tools.build:gradle:4.2.1')
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
方法里面的{}这个是叫闭包(closure) Java没有这种东西 有一种类似的叫lambda
Java的方法只能调用,因为这又是稍后被调用的回调方法。现在调不行,只能传递方法本身,让你本身去调用。Java不允许传递方法,这里Java为了实现方法传递,包了一层,把方法包进了一个匿名类对象里面,用对象传递过去。
int methodA(){
return 1;
}
void methodB(){
//preOperate
//在Java中 我们可以去调用方法 如果这个时候想要这里使用多个其他类似的方法 我们可以进行以下操作
methodA()
//afterOperate
}
int methodA(){
return 1;
}
int methodC(){
return 1;
}
//4 如果方法不能一开始就调用呢 需要执行一部分程序再调用 这个时候就需要传入方法
void methodB(int num){
//preOperate
//1 如果这里需求变了 有很多类似的方法 对使用的方法不确定
num
//afterOperate
}
methodB(methodA()) //2 我们可以调用不同方法进行传值使用
methodB(methodC()) //3 MethodB 可以用到不同的方法
int methodA(){
return 1;
}
void methodB(Method someMethod){
preOperate()
//1 如果这里需求又变了 有很多类似的方法 对使用的方法不确定
//我们想在方法内的任意处调用,而不是一开始将方法调用后再传过来
someMethod()
afterOperate()
}
//2 传递方法
methodB(methodA)
methodB(MethodC)
interface Foo {
//this is the interface declaration
fun print()
}
class Bar : Foo {
//Bar is an implementing class
override fun print() {
println("Bar")
}
}
fun test(foo: Foo) { //the method accepts an interface
foo.print() //and invokes it, the implementing class' print() will be invoked.
}
fun main(args: Array<String>) {
test(Bar()) //invoke test() with the class implementing the interface
}
fun main(args: Array<String>) {
test(object : Foo {
override fun print() {
}
}) //invoke test() with the class implementing the interface
}
闭包 closure
在 groovy中 可以传递方法 可以被传递的方法 传递的是方法本身 如果闭包是最后一个参数 可以不加括号。
{ [closureParameters ->] statements}
[closureParameters ->] 参数列表部分
statements 语句部分
methodMissing
有的时候、我们点开方法、会发现类中没有定义相关的方法 。但是这个时候程序依旧可以运行、是因为groovy中有这个methodMissing。类中没有定义的方法 如果符合其中要求、将进行调用
public Object methodMissing(String name, Object args) {
Configuration configuration = configurationContainer.findByName(name)
if (configuration == null) {
if (!getMetaClass().respondsTo(this, name, args.size())) {
throw new MissingMethodException(name, this.getClass(), args);
}
}
Object[] normalizedArgs = GUtil.collectionize(args)
if (normalizedArgs.length == 2 && normalizedArgs[1] instanceof Closure) {
return doAdd(configuration, normalizedArgs[0], (Closure) normalizedArgs[1])
} else if (normalizedArgs.length == 1) {
return doAdd(configuration, normalizedArgs[0], (Closure) null)
}
normalizedArgs.each {notation ->
doAdd(configuration, notation, null)
}
return null;
}
def demo = new Demo()
//这里可以调用test方法 并输出 虽然没有定义方法、但是符合methodMissing中的要求
demo.test()
class Demo{
void methodMissing(String name,Object... args){
if (name == "test"){
println("test")
}
}
}
buildTypes 构建类型
可以在构建类型中设置不同的构建类型。在src目录下创建相应的文件。如下图所示。gradle会将不同构建类型的目录下的文件和main目录中的文件进行整合打包。不同构建类型下的文件不要和main目录(主目录)下的文件冲突。
buildTypes {
debug{
}
release{
}
internal{
}
}
我们可以通过切换不同active build variant 去切换不同构建类型
如果工程还需要细化打包方向的话。还可以通过定义flavorDimensions 和productFlavors
- productFlavors 产品定位 产品走向
- flavorDimensions 定位维度 ( 如消费维度 国家维度(像出海应用和国内应用))
可以通过以下代码类比配置
flavorDimensions 'cost', 'nation', 'age'
productFlavors {
free{
dimension 'cost'
}
charge{
dimension 'cost'
}
china{
dimension 'nation'
}
global{
dimension 'nation'
}
minor{
dimension 'age'
}
adult{
dimension 'age'
}
}
这个时候我们可以更加细化打包内容 、将不同维度、不同环境的内容打到一个包中。
我们可以通过切换不同active build variant 去切换不同构建类型
compile implementation api
配置如下
//app module 0级
implementation project(":library1")
//library1 module 1级
implementation project(":library2")
library2 module 2级
app模块依赖library1、library1依赖library2。那么app依赖library2(传递依赖)。app模块可以用library2的代码 如果app 用 retrofit 。retrofit中依赖了okhttp。 如果没有用传递依赖、那么app还要单独依赖okhttp。 但是有的时候我们进行三方库开发。内部使用了某些其他的库进行一些和功能使用无关的封装操作、我们并不想将这些操作暴露给其他模块。这个时候这种传递依赖就不好了。会对其他开发者使用造成困扰。
传递依赖还会导致依赖的模块重新编译、如library2改动、app需要重新编译 (.java 变成 .class 不是打包)没有传递依赖的话打包总时长快一些。
没有传递依赖导致的不需要重新编译对本地项目收益较大(不是主module)
- compile、api 会传递依赖,api是compile的替代品 效果一样
- implementation 不会传递依赖
Gradle Wrapper
一个自动配置,如果本地没有gradle ,下载一个用。否则用本地的 可以通过CMD输入gradlew进行执行
有Gradle Wrapper 就不需要每一个工程放一个gradle进去,只需要gradle进行配置。指定需要的版本进行下载就好了。不用每个工程都将gradle放入。通过gradle-wrapper.properties配置
settings.gradle
项目里的结构 对项目中的module进行列举
task
任务task
task创建
//可以这样
Task demo = task(demo){
println("Configuration")
}
demo.doLast{
println("demo ")
}
----------------------
//这样也行 不定义对象接收task
task clean(type: Delete) {
println 2
delete rootProject.buildDir
println 3
}
task执行内容 我们可以看一下以下代码的运行流程对task运行流程进行了解
task clean(type: Delete) {
println 2
delete rootProject.buildDir
println 3
doLast {
println 4
}
doFirst {
println 1
}
}
执行 gradlew 运行结果如下
欸嘿 我们可以看到执行gradlew 输出了一下信息(这个时候我们没有执行clean任务 为什么输出了3?),我们可以看到输出的内容上面有一个Configuration project。这里在对工程进行配置。而这里输出的是配置信息。
执行 gradlew clean 运行结果如下
输出了配置信息 然后输出了doLast 和 doFirst中的内容
- doLast 往配置阶段的列表里面的最后面插入任务
- doFirst 往配置阶段的列表里面的最前面插入任务
那么 这段代码的执行结果是?
task clean(type: Delete) {
println 2
delete rootProject.buildDir
println 3
doLast {
println 4
}
doFirst {
println 1
}
doLast {
println 5
}
doFirst {
println 6
}
}
输入结果如下所示
- 配置信息 输出 2 3
- clean task 中
- 往最后插入一个输出4的任务| //4
- 往最前面插入一个输入1的任务| 1//4
- 往最后面插入一个输出5的| 1//4 5
- 往最前面插入一个输出6| 6 1//4 5
因此。我们配置gradle task 。不是在task clean(type: Delete) {//}进行编写。在这里不管调用与否,都会执行(在project 配置阶段 )。我们需要在task clean(type: Delete) { doFirst{ // }} 或task clean(type: Delete) { doLast{ // }}中对任务进行编写 。这里面编写的内容会在task执行阶段运行。
dependsOn
任务依赖会决定任务运行的先后顺序,被依赖的任务会在定义依赖的任务之前执行、创建任务间的依赖关系如下所示
task hello{
doLast {
println("hello")
}
}
// hello 被 world 依赖
task world(dependsOn: hello){
doLast {
println("world")
}
}
//gradlew world 输出信息如下 hello会先执行
> Task :hello
hello
> Task :world
world
gralde执行的生命周期
- 初始化阶段 执行settings.gradle 确定工程的主project 和 子project
- 定义阶段 执行每个project的build.gradle 确定所有的task所组成的有向无环图
- 执行阶段 根据上一阶段得到的有向无环图,依据有向无环图来执行指定的task
在生命周期的不同阶段插入代码
- 初始化阶段与定义阶段之间 即第一个结束之后 第二个开始之前 在setting.gradle的最后编写代码
- 在定义阶段和执行阶段之间 可以在以下代码块中插入相关代码 (和网络 文件相关的操作)
afterEvaluate {
// 插入定义阶段和执行阶段之间的代码
}