gradle project详解

1. project了解

Project在gradle中包括根Project和子Project,例如下面的项目包括3个project,分别是app,module1,module2.
 gradle project详解_第1张图片
我们可以在命令上输入以下命令验证一下:

gradlew project

输出:
 gradle project详解_第2张图片

而且每一个project下都包含一个build.gradle文件,这是一个Project的标配,Project的配置和管理,最终都是由build.gradle文件实现。

2. project 核心API讲解

2.1. project 核心api概述

project的api可以使用一下一张图进行总结:
 gradle project详解_第3张图片
project相关api:让当前project拥有操作父project以及管理子project的能力
task相关api:为当前project提供新增task以及使用当前project已有task的能力
属性相关api:让我们拥有为project添加额外属性的能力
file相关api:操作当前project下文件的处理
生命周期api:和gradle生命周期相关的api
其他api:除上述api以外比较零散的api

2.2. project相关api详解

每个project都会对应一个build.gradle,每一个groovy脚本都会被编译器编译成script字节码,每个build.gradle都会被gradle编译成project字节码。我们在build.gradle中所写的所有逻辑就相当于在project类内部进行书写。

2.2.1. getAllprojects()

输出所有的的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的配置阶段执行的。

2.2.2. getSubprojects()

获取当前项目的所有子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
--------------------------

2.2.3. gatParent()

获取父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

2.2.4. getRootProject()

获取根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()获取所有的子工程节点。

2.2.5. project()

可以在父工程中完成子工程的所有配置,传入的参数时相对于根工程的,传入app时表示直接从根工程下面查找该名称。

project('app') { Project project ->
    apply plugin : 'com.android.application'
    group 'com.test'
    version '1.0.0'
    dependencies {

    }

    android {

    }
}

2.2.6. allprojects()

配置当前节点工程和其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

2.2.7. subprojects()

不包括当前节点工程,只包括它的subproject

subprojects { Project project ->
    if(project.plugins.hasPlugin('com.android.library')) {
        apply from : './publishToMavent.gradle.'
    }
}

2.3. 属性相关api

可以打开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开始为我们定义了一系列的属性。

2.3.1. ext定义扩展属性

在一个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已经存在的方法同名,否则编译的时候不报错,但是在使用时会提示属性找不到。

2.4. 文件相关api

2.4.1. 路径获取相关api

// 根工程文件路径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

2.4.2. 文件操作相关api

2.4.2.1. 文件定位

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进行遍历处理。

2.4.2.2. 文件拷贝

把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进行重命名。


2.4.2.3. 文件树遍历

文件树遍历使用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。

3. 依赖相关api

3.1. buildscript

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 // 禁止传递依赖
}

3.2. compile()

使用compile()引入的库,在打包的时候类和资源都会打包在apk中。

3.3. provided()

占位编译,在编译的时候gradle会读取引入第三方库的类和资源,但是在打包的时候是不会将这个库的类和资源打入到输出工程中的。在以下2中场景下使用provided()
1. 只使用第三方库生成的类,第三方库只在编译的时候起作用就可以了
2. 当前工程如果是类库,引入了主工程相同版本的库

你可能感兴趣的:(gradle)