Gradle 带你玩转Project

1,035 阅读7分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第4天,点击查看活动详情

前言

本打算用gradle尝试着去写一些脚本方便项目的构建和打包,但是在复习gradle的时候有了一些新的理解,对gradle的理解不敢说精通,但也更透彻了,所以打算从新写一个系列的文章来做个总结。 按照官方文档 docs.gradle.org/current/dsl… 来熟悉Gradle中的Project。

浏览Gradle官方文档

ba41f8ef719c55e717e4be9d220ef4d.png

看到有很多内容,因为不止有android用到gradle嘛,左边还有个目录,入门那些的我们就不管了,进阶的也不管了,毕竟看到这里的多多少少也是写过一些gradle代码的,这章我们主要看这两个

6291a017b95f0aed930a46e66821ec5.png

Gradle构建的生命周期

先简单讲讲生命周期吧,一些简单的东西我就不讲很详细了,比如一个项目有settings.gradle、根目录的build.gradle和模块build.gradle这些,就不详细讲了。
看官网给出的介绍

4cd8ca1cb864f2ddf30cd402bf40bc8.png

看不懂没关系,至少我们能肯定的是构建过程有3个生命周期,Initialization、Configuration和Execution,我建议记住这3个单词,而不要总用中文的初始化、构建、执行去理解。官网下了还良心的给出了个Demo,告诉我们每个生命周期执行的阶段

cc533d7a448031e45357e40f0c83c19.png

我不会kotlin所以就只讲groovy,其实差不多的。
Initialization阶段会执行settings里面的代码,Configuration阶段会执行所有build.gradle里面的操作,无论是不是task,Execution阶段会执行action里面的操作 (不懂什么是task什么是action的也别急,这里先了解下,往后学就知道了) 所以这个Demo下面的内容我们不看,学其它的东西再回来看就豁然开朗了。

Project

我们直接讲Project,我记得我刚学gradle的时候经常看到有人写文章说gradle最重要的是Project、task和action,其实这样说确实能让新人很容易上手,却同样也很容易误导别人。
但是从Project入手是不会有错的,真的了解Project是什么之后,你就真正的能看出gradle的架构,到时候task、action什么的就一目了然了。
在上面截图的地方点击进入DSL参考

46e640d2c625e01613f958767d20ab6.png

进来后左边目录有什么 Build script blocks、Core types、 Publishing types、 Container types 之类的,内容很多,但是我们都不管,我们就看project,点击project

43ac6fdf64bdb573d8790a7c10fffe7.png

进来之后看到Project下面有些概念,英文不好的朋友可以翻译看看这些概念,但是还是要记住他们的单词。

project基本概念

(1)Lifecycle 生命周期
(2)Tasks 任务
(3)Dependencies 依赖
(4)Multi-project Builds 多项目构建
(5)Plugins 插件
(6)Properties 属性
(9)Methods 方法

生命周期

生命周期是说,project在Initialization阶段被创建,我们经常在settings.gradle写比如

include ':app'

在Initialization阶段执行这行代码,创建app这个模块的project对象,那就是说一般来说,每个module对应一个project对象。准确来说是gradle根据settings.gradle去找对应module的build.gradle来创建project对象,也就是说project就是build.gradle。
所以project对象对应的代码在哪写,project在哪里创建,这些应该都很清楚了。

Methods 方法

task放到之后再写,我更倾向于单独拿一章出来写,其它的也应该分出来讲,要从浅入深,所以先讲Methods,因为它简单。
把project当成一个对象,那这个对象里面有方法就很容易理解吧,先看看一般AS生成的build.gradle是怎样的,也就是project一般是怎样的(我随便找个Demo里面的app模块的代码)

apply plugin: 'com.android.application'

android {
    compileSdkVersion 27
    buildToolsVersion "27.0.0"
    defaultConfig {
        applicationId "com.tencent.tmgp.yybtestsdk"
        minSdkVersion 14
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar','*.aar'])
}

我们先不要看android {......}里面的内容,这是plugin(插件)的知识,过滤掉之后

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar','*.aar'])
}

是的,整个project里面只剩一个dependencies 方法,这里多嘴说一下,为什么说是方法。这个是一个闭包的写法,不知道的话建议先看看groovy语法中的闭包,一个参数的情况下是能省略括号的。

然后看看project中有什么方法,其实还挺多的

4cbd5cdf603751cec5e9d7331fa128c.png

截图截不完,具体的可以看官网的API docs.gradle.org/current/jav…

我们随便拿一个方法来说,比如说getName,获取project的名称
就这样写

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar','*.aar'])
}

println this.getName()

this就是指这个project嘛,代码会在Configuration生命周期中执行,打印

af4edcf7c14b2c2691e2606aba6efe4.png

嗯,我这个Module的名称就叫app,看得不过瘾没关系,我们可以再演示调用多一点的方法

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar','*.aar'])
}

println this.getName()
println this.getPath()
println this.getRootDir()
println this.getVersion()

打印结果

5e79e597120315a693f53d137a7d138.png

而且也能在文档中找到默认生成的dependencies

523aac2cffb038123bb3a5f87f55a0a.png

看到了吧,传的参数是一个闭包(至于闭包里面写什么内容,那就要参照官网文档了)
再比如文档中还有个ant(Closure configureClosure)方法,那你就能这样写

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar','*.aar'])
}

ant{
  ...........
}

那是不是只能在build.gradle里面调用文档中的方法,当然不是,当然也可以自己定义方法

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar','*.aar'])
}

println this.getName()
println this.getPath()
println this.getRootDir()
println this.getVersion()

// 定义一个打印的方法
def myfuction(String msg){
    println msg
}
myfuction("6666666")

看结果

7f4f01520cf888ded0447f25bee5e8c.png

上面演示的是在自己内部调用自己的project方法,那在别的project对象中调用该project的方法呢? 怎么有点绕,就是一号project调用二号project的方法,要怎么操作。
(为了节省时间,我拿我一个项目来说,这种情况肯定是多模块的情况)

我们先理一下思路,要在一个project调用另一个project的方法要怎么做?当然是先拿到这个project对象,然后再调用它的方法。这时候我们API找到对project的操作(多说一句,看API最好看Javadoc)

ba244489cfd4dd83427d417909c935a.png

比较分散,可能没截完全部,总之在这个API中肯定是能找到的,光看上面的这些方法就知道能拿到所有能获取到的Project,简单说几个
(1)比如getAllprojects(),返回当前的project和子project的集合(子project的意思是比如Moudule的project就是根目录project的子project)
(2)比如getParent()就是获取父project
(3)比如project​(String path)就是根据path获取project,有的人会问我怎么懂path是什么,没关系,你可以在对应的project中调用getPath()打印,就知道这个project的path是什么了(一般都是:app)。
还有很多方法能获取到相对应的project。
假如我在名为test的project中调用:app的project

println "测试 "+project(":app").getRootDir()

获取:app的根目录的地址,这样就行了,看得出什么不。
project(":app").getRootDir() 和 this,getRootDir() 得到的结果是一样的,因为他们都是同一个根目录下。

Properties

文档中也有个Properties文档

e30010a846f93495c04093d714e4602.png

截不完图,详细的可以看文档,比如属性中有个常用的属性name,可以直接在project中获取。

println "属性name "+name

如果我们直接设值的话呢

name = "mytest"
println this.getName()

81b634d1e14e4a9032ee516959d55b7.png

会提示这个属性是只读的属性。
想看关于这些属性更详细的信息的话,可以在文档下面找到Property details

7ecad372455e1a5f69571f02e6e786a.png

你会发现属性后面有个只读的提示,name属性也是,所以无法直接这样修改属性。

还有有个额外属性操作,就是ext关键字。
在A的project中定义

ext.testStr = "test 66666666666666"

在B的project中调用

println project(":app").ext.testStr

这里先简单了解下这些原有的属性就行,在讲到插件的时候会解释更多。

总结

这是我以前写的一篇旧文章 www.jianshu.com/p/df44146d6… ,将它翻出来是因为最近有打算详细的去看kts,所以需要复习一下gradle的一些基础知识。 project就大概先提到这些吧,其它内容在讲到task、dependencies和plugin也会提到一些相关的内容,学了project的生命周期和一些属性、方法之后,对project相信也会有一定的了解。