Project在gradle中包括根Project和子Project,例如下面的项目包括3个project,分别是app,module1,module2.
我们可以在命令上输入以下命令验证一下:
gradlew project
而且每一个project下都包含一个build.gradle文件,这是一个Project的标配,Project的配置和管理,最终都是由build.gradle文件实现。
project的api可以使用一下一张图进行总结:
project相关api:让当前project拥有操作父project以及管理子project的能力
task相关api:为当前project提供新增task以及使用当前project已有task的能力
属性相关api:让我们拥有为project添加额外属性的能力
file相关api:操作当前project下文件的处理
生命周期api:和gradle生命周期相关的api
其他api:除上述api以外比较零散的api
每个project都会对应一个build.gradle,每一个groovy脚本都会被编译器编译成script字节码,每个build.gradle都会被gradle编译成project字节码。我们在build.gradle中所写的所有逻辑就相当于在project类内部进行书写。
输出所有的的project。
this.getProjects()
def getProjects() {
println '--------------------------'
println 'Root Project'
println '--------------------------'
this.getAllprojects().eachWithIndex { Project project, int index ->
if(index == 0) {
println "Root project:'${project.name}'"
}else {
println "+---- project:'${project.name}'"
}
}
}
执行gradle任务都会执行build.gradle相关配置,执行gradlew clean,输出结果:
> Configure project :
--------------------------
Root Project
--------------------------
Root project:'gradletest'
+---- project:'app'
+---- project:'module1'
+---- project:'module2'
build.gralde中的方法,代码都是在project的配置阶段执行的。
获取当前项目的所有子project:
this.getProjects()
def getProjects() {
println '--------------------------'
println 'Sub Project'
println '--------------------------'
this.getSubprojects().eachWithIndex { Project project, int index ->
println "+---- project:'${project.name}'"
}
}
输出结果:
> Configure project :
--------------------------
Sub Project
--------------------------
+---- project:'app'
+---- project:'module1'
+---- project:'module2'
上面的代码是写在根project,gradletest下面的build.gradle下面的,如果写在app的build.gradle里面输出如下:
> Configure project :app
--------------------------
Sub Project
--------------------------
获取父project,由于gradletest没有父project,所以测试代码写在子project下:
this.getParentProject()
def getParentProject() {
def name = this.getParent().name
println "this parent project name is: ${name}"
}
输出结果:
> Configure project :app
this parent project name is: gradletest
获取根project:
this.getRootPro()
def getRootPro() {
def name = this.getRootProject().name
println "this root project name is: ${name}"
}
输出结果:
> Configure project :app
this parent project name is: gradletest
project是以树的数据结构来管理的,当在根project的build.gradle获取父project时,由于当前节点已经是根节点,所以返回父节点为null,使用getRootProject()返回的总是根节点。
通过gradle管理的工程都会有一个根工程,根工程负责管理所有的子工程。子工程可以通过getParent()获取父工程节点,父工程可以通过getSubProject()获取所有的子工程节点。
可以在父工程中完成子工程的所有配置,传入的参数时相对于根工程的,传入app时表示直接从根工程下面查找该名称。
project('app') { Project project ->
apply plugin : 'com.android.application'
group 'com.test'
version '1.0.0'
dependencies {
}
android {
}
}
配置当前节点工程和其subproject的所有project,在根build.gradle配置下面代码:
// 配置当前节点工程和其subproject的所有project
allprojects {
group 'com.test'
version '1.0.0-release'
}
println project('module1').group
使用gradlew clean命令,输出结果如下:
> Configure project :
com.test
不包括当前节点工程,只包括它的subproject
subprojects { Project project ->
if(project.plugins.hasPlugin('com.android.library')) {
apply from : './publishToMavent.gradle.'
}
}
可以打开Project.java查看Project的相关属性,gradle 4.6的路径是:
.gradle\wrapper\dists\gradle-4.10.1-all\455itskqi2qtf0v2sja68alqd\gradle-4.10.1\src\core-api\org\gradle\api
Project.java开始为我们定义了一系列的属性。
在一个build.gradle里,可以通过变量定义来实现相关字符串的替换,比如:
apply plugin: 'com.android.application'
def mCompileSdkVersion = 28
def libAndroidAppcompat = 'com.android.support:appcompat-v7:28.0.0'
android {
compileSdkVersion mCompileSdkVersion
}
dependencies {
implementation libAndroidAppcompat
}
gradle支持扩展属性,通过扩展属性也可以达到上述的目的:
apply plugin: 'com.android.application'
// 扩展属性
ext {
compileSdkVersion = 28
libAndroidAppcompat = 'com.android.support:appcompat-v7:28.0.0'
}
android {
compileSdkVersion this.compileSdkVersion
}
dependencies {
implementation this.libAndroidAppcompat
}
为每个子工程配置扩展属性,在根build.gradle中添加如下代码,子工程相关配置删除。
subprojects {
ext {
compileSdkVersion = 28
libAndroidAppcompat = 'com.android.support:appcompat-v7:28.0.0'
}
}
上述方法相当于每个子project定义了扩展属性,如果想定义一份,需要把根build.gradle改成如下:
ext {
compileSdkVersion = 28
libAndroidAppcompat = 'com.android.support:appcompat-v7:28.0.0'
}
子工程build.gradle改成如下:
apply plugin: 'com.android.application'
android {
compileSdkVersion this.rootProject.compileSdkVersion
}
dependencies {
implementation this.rootProject.libAndroidAppcompat
}
如果把rootProject去掉,也是可以的
apply plugin: 'com.android.application'
android {
compileSdkVersion this.compileSdkVersion
}
dependencies {
implementation this.libAndroidAppcompat
}
gradle规定,父project所有的属性都会被根project继承,所以可以直接在子project使用父project的属性。
可以把所有的扩展属性定义到一个独立的gradle文件中,在需要使用的build.gradle文件中使用apply from进行引入。
apply from : file('common.gradle')
2.3.2. gradle.properties定义属性
扩展属性也可以定义在gradle.properties中,在这个文件中只能定义key-value形式的扩展属性,而不能使用类似Map方式的定义,在使用上有一定的限制。
下面通过在gradle.properties定义开关控制一个模块是否引入项目中,在gradle.properties中定义:
isLoadTest = false
setting.gradle:
include ':app', ':module1', ':module2'
if(hasProperty('isLoadTest') ? isLoadTest.toBoolean() : false) {
include 'test'
}
所以gradle.properties也可以定义扩展属性,在使用的时候转换成对应的类型。
例如在gradle.properties定义:
mCompileSdkVersion = 28
使用的时候:
compileSdkVersion mCompileSdkVersion.toInteger()
在gradle.properties定义的属性不能和build.gradle已经存在的方法同名,否则编译的时候不报错,但是在使用时会提示属性找不到。
// 根工程文件路径api
println "the root file path is: " + getRootDir().absolutePath
// 获取build文件路径
println "the build file path is: " + getBuildDir().absolutePath
// 获取当前工程路径
println "the project file path is: " + getProjectDir().absolutePath
输出结果:
> Configure project :
the root file path is: E:\studio\gradletest
the build file path is: E:\studio\gradletest\build
the project file path is: E:\studio\gradletest
file()方法
在当前project下查找对应的文件,比如当前gradle文件是跟工程,使用file方法,就会从根工程下面查找对应的文件。如果查找到了,返回对应的file对象,如果查找不到,报异常。
输出common.gradle的内容,根工程build.gradle添加:
println getContent('common.gradle')
def getContent(String path) {
try {
def file = file(path)
return file.text
}catch(GradleException e){
println 'file not found'
}
}
输出结果:
ext {
// compileSdkVersion = 28
libAndroidAppcompat = 'com.android.support:appcompat-v7:28.0.0'
}
file()参数传入的路径是相对于当前工程的,和使用new file()的区别是new File()对于传入的参数不会自动转成相对于当前工程的相对路径,而file会自动转换成相对当前工程的相对路径。
files()方法
files()定义一个或多个文件,返回一个collection,我们可以通过collection进行遍历处理。
把app工程下的build.gradle拷贝到根工程的build目录下,在app的build.gradle添加代码:
copy {
from file('build.gradle')
into getRootProject().getBuildDir()
}
也可以拷贝整个文件夹:
copy {
from file('build/outputs/apk/')
into getRootProject().getBuildDir().path + '/apk/'
}
也可以在拷贝的时候使用exclude进行排除,使用rename进行重命名。
文件树遍历使用fileTree实现:
fileTree('build/outputs/apk/'){ FileTree fileTree ->
fileTree.visit { FileTreeElement element ->
println 'the file name is: ' + element.file.name
copy {
from element.file
into getRootProject().getBuildDir().path + '/test/'
}
}
}
上述提供的文件操作API只是针对在本工程进行文件操作,如果需要跨工程进行文件操作,需要使用groovy提供的api。
buildscript { ScriptHandler scriptHandler ->
// 配置工程的仓库地址
scriptHandler.repositories { RepositoryHandler repositoryHandler ->
repositoryHandler.jcenter()
repositoryHandler.mavenCentral()
repositoryHandler.mavenLocal()
repositoryHandler.ivy {}
repositoryHandler.maven {
name 'personal'
url 'http://localhost:8081:/nexus/repositories/'
credentials {
username = 'admin'
password = 'admin123'
}
}
}
// 配置工程的"插件"地址
scriptHandler.dependencies {
classpath 'com.android.tools.build:gradle:2.2.2'
}
}
buildscript里面的dependencies和子工程下的dependencies是不一样的,buildscript里面的dependencies是表示gradle本身对第三方的依赖,子工程下的dependencies是应用程序对第三方的依赖。
子工程的dependencies可以添加依赖类型:
// 本地
implementation fileTree(include: ['*.jar'], dir: 'libs')
// 远程jar包
implementation 'com.android.support:appcompat-v7:28.0.0'
// 依赖library工程
implementation project(':module1')
依赖冲突的解决,使用exclude进行排除,如在引入包的后面添加
{
exclude module: 'support-v4' // 排除依赖
exclude group: 'com.android.support'
transitive false // 禁止传递依赖
}
使用compile()引入的库,在打包的时候类和资源都会打包在apk中。
占位编译,在编译的时候gradle会读取引入第三方库的类和资源,但是在打包的时候是不会将这个库的类和资源打入到输出工程中的。在以下2中场景下使用provided()
1. 只使用第三方库生成的类,第三方库只在编译的时候起作用就可以了
2. 当前工程如果是类库,引入了主工程相同版本的库