Android Gradle学习(四) Project详解

每一个 build.gradle 脚本文件被 Gradle 加载解析后,都会对应生成一个 Project 对象,在脚本中的配置方法其实都对应着 Project 中的API,如果想详细了解这些脚本的配置含义,有必要对 Project 类做些深入的剖析。

1. Project类图

当构建进程启动后,Gradle基于build.gradle中的配置实例化org.gradle.api.Project类,先来看看 Project 类的主要结构(节选部分常用):
Android Gradle学习(四) Project详解_第1张图片
接下来我们通过一些实际的例子,由浅入深的来体会这些 API 的含义。

2. getter/setter属性

File bd = getBuildDir()
println "buildDir = ${bd.getAbsolutePath()}"

//获取Project的名字
String name = getName()
println "project name = $name"

//设置Project的描述信息
setDescription "这是一个测试案例"
String desc = getDescription()
println "project description = $desc"

//获取Project的路径
String path = getPath();
println "project path = $path"

class VersionInfo {
    String version
    boolean release

    VersionInfo(String v, boolean release) {
        version = v
        this.release = release
    }

    String toString() {
        return "V-${version}-${release ? 'release' : 'debug'}"
    }
}
//设置Project的版本号,参数可以是任何对象,gradle内部会使用 toString() 方法返回的值
setVersion(new VersionInfo("1.0.0", true))
println("project version = ${getVersion()}")

//设置Project的分组
setGroup "TestGroup"
println("project group = ${getGroup()}")

直接执行 gradle 命令,可以看到在配置阶段输出以下结果:

> Configure project :
rootDir = /Users/hjy/Desktop/gradle
buildDir = /Users/hjy/Desktop/testgradle/build
project name = gradle
project description = 这是一个测试案例
project path = :
project version = V-1.0.0-release
project group = TestGroup

2. 创建task

我们在 build.gradle 文件中定义 task 的方式,其实都对应的是 Project 类中的 task() 方法,从 Project 类图中的 task API 可以看到有多种不同的形式,前面介绍 task 的章节已经介绍过了,这里就不赘述了。

3. 文件操作

3.1 通过mkdir创建目录

File mkDir = mkdir("${buildDir}/test");
File mkDir2 = mkdir("${buildDir}/test2")
println "检测目录是否创建成功:${mkDir.exists()}, ${mkDir2.exists()}"

3.2 通过file、files 定位文件

//定位单个文件,参数可以是相对路径、绝对路径
File testDir = file("${buildDir}/test")
println "文件定位是否成功:${testDir.exists()}"

//文件集合,Gradle里用 FileCollection 来表示
FileCollection fileCollection = files("${buildDir}/test", "${buildDir}/test2")
println "-------对文件集合进行迭代--------"
fileCollection.each {File f ->
    println f.name
}
println "-------文件迭代结束-------"
//获取文件列表
Set set = fileCollection.getFiles()
println "文件集合里共有${set.size()}个文件"

3.3 通过fileTree创建文件树

Gradle里用 ConfigurableFileTree 来表示文件树,文件树会返回某个目录及其子目录下所有的文件,不包含目录。

//先在build目录下创建3个txt文件
file("${buildDir}/t1.txt").createNewFile()
file("${buildDir}/test/t2.txt").createNewFile()
file("${buildDir}/t1.java").createNewFile()

//1.通过一个基准目录创建文件树,参数可以是相对目录,也可以是绝对目录,与file()方法一样
println "通过基准目录来创建文件树"
ConfigurableFileTree fileTree1 = fileTree("build")
//添加包含规则
fileTree1.include "*.txt", "*/*.txt"
//添加排除规则
fileTree1.exclude "*.java"
fileTree1.each { f ->
    println f    
}

//2.通过闭包来创建文件树
println "通过闭包来创建文件树"
ConfigurableFileTree fileTree2 = fileTree("build") {
    include "*/*.txt", "*.java"
    exclude "*.txt"
}
fileTree2.each { f ->
    println f    
}

//3.通过map配置来创建文件树,可配置的选项有:dir: ''、include: '[]、exclude: []、includes: []、excludes: []
println "通过Map来创建文件树"
def fileTree3 = fileTree(dir: "build", includes: ["*/*.txt", "*.java"])
fileTree3 = fileTree(dir: "build", exclude: "*.java")
fileTree3.each { f ->
    println f    
}

3.4 复制文件

复制文件需要使用复制任务(Copy)来进行,它需要指定要复制的源文件和一个目标目录,复制的规则都是定义在 CopySpec 接口里的,更详细的说明可参见 API 文档。

task testCopyFile(type: Copy) {
    //复制build目录下的所有文件
    from "build"
    //复制单独的某个文件
    from "test.java"
    //复制某个文件树下的所有文件
    from fileTree("build")

    include "*.txt"
    include "*.java"
    exclude "t1.txt"
    //指定目标目录
    into "outputs"

    //对复制的文件重命名:通过闭包来映射
    rename { fileName ->
        //增加 rename_ 前缀
        return fileName.endsWith(".java") ? "rename_" + fileName : fileName
    }

    //通过正则来映射文件名:abctest.java 会映射成 abchjy.java
    rename '(.*)test(.*)', '$1hjy$2'
}

3.5 删除文件

//删除 build 目录下所有文件
delete("${buildDir}")

4. 多项目构建

前面我们介绍的例子,都是单独执行某一个 build.gradle 文件。但是我们在 Android 应用开发中,一个 Project 可以包含若干个 module ,这种就叫做多项目构建。在 Android Studio 项目中,根目录都有一个名叫 settings.gradle 的文件,然后每个 module 的根目录中又有一个 build.gradle 文件,Gradle 就是通过 settings.gradle 来进行多项目构建的。

4.1 通过 settings.gradle 引入子项目

1.先创建如下几个目录及文件:
Android Gradle学习(四) Project详解_第2张图片
如图所示,在项目根目录创建一个 settings.gradle,在根目录、app以及library目录下也都创建一个 build.gradle 文件。

2.在 settings.gradle 里引入子项目

include ":app", ":library"

3.在 build.gradle 里增加测试代码

//在根目录 build.gradle 里增加
println "-----root file config-----"

//在 app/build.gradle 里增加
println "-----app config-----"

//在 library/build.gradle 里增加
println "-----library config-----"

4.在项目根目录执行命令 gradle -q,结果如下:

-----root file config-----
-----app config-----
-----library config-----

这是一个多项目构建的简单例子,可以看到结构与我们的 Android 项目是类似的。Gradle 在运行时会读取并解析 settings.gradle 文件,生成一个 Settings对象,然后从中读取并解析子项目的 build.gradle 文件,然后为每个 build.gradle 文件生成一个 Project 对象,进而组装一个多项目构建出来。

Settings 里最核心的API就是 include 方法,通过该方法引入需要构建的子项目。

include​(projectPaths: String...)

这里我们为每个 build.gradle 文件生成了一个 Project 对象,跟总共3个 Project,根目录的 Project 我们称之为 root project,子目录的 Project 我们称之为 child project。

4.2 项目配置

在根项目里可以对子项目进行配置:

//通过path定位并获取该 Project 对象
project(path: String): Project
//通过path定位一个Project,并进行配置
project(path: String, config: Closure): Project

//针对所有项目进行配置
allprojects(config: Closure)
//针对所有子项目进行配置
subprojects(config: Closure)

我们修改根目录 build.gradle 文件如下:

println "-----root file config-----"

//配置 app 项目
project(":app") {
    ext {
        appParam = "test app"
    }
}

//配置所有的项目
allprojects {
    ext {
        allParam = "test all project"
    }   
}

//配置子项目
subprojects {
    ext {
        subParam = "test sub project"
    }
}

println "allParam = ${allParam}"

修改 app/build.gradle 文件如下:

println "-----app config-----"
println "appParam = ${appParam}"
println "allParam = ${allParam}"
println "subParam = ${subParam}"

修改 library/build.gradle 文件如下:

println "-----library config-----"
println "allParam = ${allParam}"
println "subParam = ${subParam}"

运行结果如下:

-----root file config-----
allParam = test all project
-----app config-----
appParam = test app
allParam = test all project
subParam = test sub project
-----library config-----
allParam = test all project
subParam = test sub project

5. 构建脚本配置

5.1 buildscript

配置该 Project 的构建脚本的 classpath,在 Andorid Studio 中的 root project 中可以看到:

buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'
    }
}

5.2 apply

apply(options: Map)

我们通过该方法使用插件或者是其他脚本,options里主要选项有:

  • from: 使用其他脚本,值可以为 Project.uri(Object) 支持的路径
  • plugin:使用其他插件,值可以为插件id或者是插件的具体实现类

例如:

//使用插件,com.android.application 就是插件id
apply plugin: 'com.android.application'
//使用插件,MyPluginImpl 就是一个Plugin接口的实现类
apply plugin: MyPluginImpl

//引用其他gradle脚本,push.gradle就是另外一个gradle脚本文件
apply from: './push.gradle'

6. 属性

6.1 Gradle属性

在与 build.gradle 文件同级目录下,定义一个名为 gradle.properties 文件,里面定义的键值对,可以在 Project 中直接访问。

//gradle.properties里定义属性值
company="hangzhouheima"
username="hjy"

在 build.gradle 文件里可以这样直接访问:

println "company = ${company}"
println "username = ${username}"

6.2 扩展属性

还可以通过 ext 命名空间来定义属性,我们称之为扩展属性。

ext {
  username = "hjy"
  age = 30
}

println username
println ext.age
println project.username
println project.ext.age

必须注意,默认的扩展属性,只能定义在 ext 命名空间下面。对扩展属性的访问方式,以上几种都支持。

相关文章
Android Gradle学习(一) Gradle基础入门
Android Gradle学习(二) 如何创建Task
Android Gradle学习(三) Task进阶学习
Android Gradle学习(四) Project详解
Android Gradle学习(五) Extension详解
Android Gradle学习(六) NamedDomainObjectContainer详解
Android Gradle学习(七) Gradle构建生命周期
Android Gradle学习(八) 统计Task执行时长

你可能感兴趣的:(Android,Groovy&Gradle)