Skip to content

Latest commit

 

History

History
616 lines (448 loc) · 17.5 KB

4.Project.md

File metadata and controls

616 lines (448 loc) · 17.5 KB
title date categories tags description
Project
2021-01-23
Project
Project

Settings文件

在Gradle 中,定义了一个设置文件,默认名:settings.gradle,放在根目录下

settings.gradle的主要作用是为了配置子工程。

在Gradle 中, 多工程是通过工程树来表示。就相当于我们在Android 中的 Project 和 Module 一样。根工程(根目录,rootProject)就相当于Android 中的Project。然后,一个根工程有多个子工程,子工程就相当于Android中的Module一样。

一个子工程只有在settings.gradle 文件中配置过,才能被找到,才能在构建的时候被包含进去。

我们可以看一下,在我们Android 工程中的settings.gradle长什么样子:

可以看到,最外层的My Application 就是 根工程rootProject。在根工程中有两个子工程,分别是app 和 common。 在不明确指定子工程路径的情况下,默认目录就是同级目录。可以看到,我的 app 目录和 common目录都是同 setting.gradle文件同一目录的,所以可以不指定目录了。

如果想要指定目录:

project(':app').projectDir = new File()

project(':app') 是使用project方法,查找名字为app的子工程。 返回值就是 app 子工程对象,然后修改这个子工程的 projectDir 属性值。这样就可以了。

TIP: 好像冒号 : 就表示根工程 冒号+xx 就表示子工程

Build文件

每个 Project 都有一个 build.gradle 文件。

gradle 构建的时候,每一个 build.gradle 脚本文件被 Gradle 加载解析后,都会对应生成一个 Project 对象。所以在 build.gradle 里写的 DSL,其实都是 Project 接口的一些方法,Project 其实是一个接口,真正的实现类是 DefaultProject。后面看Project的类图

RootProject可以获取到所有的ChildProject,因此可以在RootProject中对ChildProject进行统一配置和管理。

subprojects {
    repositories {
        jcenter()
    }
}

配置项目中所有的ChildProject的依赖的仓库为jcenter

allprojects {
    repositories {
        jcenter()
    }
}

配置项目中所有的Project的依赖的仓库为jcenter,包括RootProject和ChildProject

参考:https://blog.csdn.net/u013700502/article/details/85231687

subprojects allprojects 其实是两个方法,接受闭包作为参数。这个方法会对工程进行遍历,遍历的过程中会调用我们自定义的闭包,所以,我们可以在闭包里配置、打印或者修改Project属性都可以。

buildscript 和 allprojects

这里我们要明确这两个配置。

  1. buildscript 配置的 是gradle脚本自身需要使用的资源。
  2. build.gradle 文件中直接声明的依赖项、仓库地址等信息是项目自身需要的资源
  3. gradle在执行脚本时,会优先执行buildscript代码块中的内容,然后才会执行剩余的build脚本。
buildscript {
    repositories {
        google()
        jcenter()
        
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.2'
        
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

看下我的 buildscript。dependencies 指的是 gradle脚本运行,需要依赖什么插件。repositories 说的是这个插件去哪里下载。然后在使用的时候,apply plugin 来引用就可以了。

在 allprojects 中的dependencies 以及 我们app的 build.gradle 中的dependencies 说的是项目的依赖项。

比如我代码想要使用一下appcompat 这个库。那就在app下的build.gradle 中的dependencies添加,代表我们需要依赖 appcompat 这个库。

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.0.0'
}

然后,这个库去哪里下载呢?就在 repositories 里配置。可以每个ChildProject单独配,也可以直接写在RootProject的repositories中,这样就是告诉gradle,项目需要的依赖,比如 appcompat 库,还有其他乱七八糟的库,都去jcenter下载去吧。

allprojects {
    repositories {
        jcenter()
    }
}

自定义属性

Project和 Task都允许用户添加额外的自定义属性。

看完这个大概就明白了

// 是在project下的
// 定义一个Project的属性
ext.age = 330

// 通过代码块,定义多个Project属性
ext{
    android = "androidandroidandroid" 
}
project.tasks.create("printVersion"){

    doLast {
        // 访问Project的age属性
        println(project.age)
        // 访问Project的android属性
        println(project.android)
        println(project.ext.android)
        println(rootProject.android)
        println(rootProject.ext.android)
        // 错误,这样写是访问Task的ext。但是我们这个printVersion Task没有android这个属性
        //println(ext.android)

        // 也可以不带 project 直接访问。默认就是project.
        println(android)

        // 这样写,说的是  task的属性,即:Task 有个 eee的属性, 注意:不是project的属性
        ext.eee = "22"
        println("${ext.eee}")
        //这样访问,肯定没有这个
        //println("${project.ext.eee}")
        println("作为task的属性: ${printVersion.eee} ")

        // 全局project.ext
        project.ext.fff = "fffff"
        project.fff = "1111";// 这样写也可以
        println("${rootProject.ext.fff}") //
        println("${rootProject.fff}") //
        //println("${ext.fff}") // 没 
    }
}

Task 与 Project

RootProject 就相当于一个我们Android 中的Application。 一个Application里有好多Module。Module就相当于ChildProject。 每个Project都可以包含多个Task。 Task就是一个任务,一个操作。

我们创建的Task,都会作为Project的一个属性来管理。属性名就是Task的名称。我们可以来验证一下。

task hello {
    doLast {
        println 'Hello world!'

        println project.hasProperty("hello") // true
    }
}

访问任务的方式

  1. 任务名. 前面说了, 我们创建的Task,都会作为Project的一个属性来管理。属性名就是Task的名称。 因此, 我们可以使用project.属性名 来访问任务, project 可以省略.
  2. TaskContianer 是用来管理所有的 Task 实例集合的,可以通过 Project.getTasks() 来获取 TaskContainer 实例。然后,我们可以通过tasks来查找任务
tasks['hello'].doLast {
    print "hello"
}

访问的时候, 任务名就是key。 其实,[]在Groovy中是一个操作符。 a[b]对应的 是 a.getAt(b)这个方法。对应的,我们tasks['hello']其实就是调用tasks.get('hello')这个方法,查看Gradle源码,可以看到,这个方法是通过tasks.findByName(String name) 来实现的。

  1. 通过路径访问
tasks.findByPath('项目名称:任务名称')
tasks.getByPath()

get 方法找不到会报错。find 找不到返回null

  1. 通过名称访问
tasks.findByName()
task.getByName()

采用路径的方式,可以传路径,也可以只传入名字。但是通过名字访问,参数只能是名字,不能是路径。

Project类图

当构建进程启动后,Gradle基于build.gradle中的配置实例化org.gradle.api.Project类,先来看看 Project 类的主要结构(节选部分常用):

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()}")

文件操作

通过mkdir创建目录

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

通过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<File> set = fileCollection.getFiles()
println "文件集合里共有${set.size()}个文件"

通过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    
}

复制文件

复制文件需要使用复制任务(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'
}

删除文件

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

多项目构建

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

通过 settings.gradle 引入子项目

  1. 先创建如下几个目录及文件:

在项目根目录创建一个 settings.gradle,在根目录、app以及library目录下也都创建一个 build.gradle 文件。

  1. 在 settings.gradle 里引入子项目
include ":app", ":library"
  1. 在 build.gradle 里增加测试代码
//在根目录 build.gradle 里增加
println "-----root file config-----"

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

//在 library/build.gradle 里增加
println "-----library config-----"
  1. 在项目根目录执行命令 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。

项目配置

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

//通过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

构建脚本配置

  1. buildscript

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

buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'
    }
}
  1. apply
apply(options: Map<String, ?>)

我们通过该方法使用插件或者是其他脚本,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'

属性

  1. Gradle属性

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

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

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

println "company = ${company}"
println "username = ${username}"
  1. 扩展属性 还可以通过 ext 命名空间来定义属性,我们称之为扩展属性。
ext {
  username = "hjy"
  age = 30
}

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

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