Gradle专题

七、Gradle中的Project

2019-01-18  本文已影响0人  ywy_袁滚滚

一、Project概念

  1. 什么是Project
  1. Project的作用
    根Project是用来统筹管理所有的子Project的,而每个子Project都对应了一个输出。比如我们的app module的类型是application的,那么它最终就对应生成了一个APK文件,是android library类型的,最终会生成一个aar文件,java library类型的生成一个jar文件等等

二、Project核心API

  1. 获取当前project及其子project


    获取project所有的child.png
  1. 获取根project


    获取项目的根project.png
  1. 获取project的子project
//调用自己定义的方法,方法中调用了gradle提供的API getSubprojects()
this.getSubProjects()

def getSubProjects() {
    //this.rootProject 获取项目的根目录,
    // getSubprojects()方法返回所有子Project的Set集合,遍历
    def subProjectSet = this.rootProject.getSubprojects()
    subProjectSet.eachWithIndex {
        Project project, int index ->
            if (index > 0 && index < projectSet.size() - 1) {
                println "+--- Project ':$project.name'"
            } else {
                println "\\--- Project ':$project.name'"
            }
    }
}

//在终端执行gradlew clean,以下是结果
'''
> Configure project :app
\--- Project ':annottations'
+--- Project ':app'
+--- Project ':compiler'
+--- Project ':core'
\--- Project ':ec'

'''

4.获取父project

//在 app build.gradle中加入以下

this.getParentProject()
def getParentProject() {
    def name = "parent为null"
    def parent = this.getParent()
    if (parent != null) { //根Project,已经是最顶层,没有parent
        name = parent.name
    }
    println "project ${this.name} 的parent为:$name"
}


//在终端执行gradlew clean,以下是输出结果
'''
> Configure project :app
project app 的parent为:ecommerce

'''

  1. project方法配置project
// Project project(String path, Closure configureClosure);

//在根project中的build.gradle
project('app') { Project project ->
    //doSomething for app project
    //比如为app 工程强制使用某个版本的依赖来解决依赖冲突中出现的依赖
    project.configurations.all {
        resolutionStrategy {
            force 'com.android.support:support-annotations:26.1.0'
        }
    }

    //指定输出
    apply plugin:'com.android.application'
    //添加group
    group 'com.github'
    //指定版本
    version '1.0.0'
    //凡是project中可以配置的都可以进行配置
    //比如添加依赖
    dependencies{

    }
    //添加android相关配置
    android{

    }
}
    
    //同样的还可以为其它的project配置,这里就不再配置了,和上面是一样的,将‘app’替换成其它module的名字,然后及对该module进行配置

//在根project中的build.gradle
//allprojects 为当前project及其所有子project配置
allprojects {
    //仓库
    repositories {
        google()
        jcenter()
    }

    //添加group
    group 'com.github'
    //指定版本
    version '1.0.0'
}

//打印从未配置过的compiler module的group看看是否配置成功
println project('compiler').group

//以下是输出结果,因为笔记添加图片太麻烦了,所以直接拷贝终端的输出结果
'''
C:\Users\***\Desktop\project\ecommerce>gradlew clean
Starting a Gradle Daemon, 2 incompatible Daemons could not be reused, use --status for details

> Configure project :
com.github


BUILD SUCCESSFUL in 16s
6 actionable tasks: 4 executed, 2 up-to-date
'''

//比如我们上传我们的library module到Maven仓库,我们的root project一般是不需要上传上去的,需要将子module上传上去,就可以写一个上传的gradle文件,然后为所有的子module引入。

//省略了project参数
subprojects {
    //库工程才需要上传到Maven
    if (project.plugins.hasPlugin("com.android.library")){
        //当一个功能比较独立时,可以写成一个单独的.gradle文件,然后再需要的地方apply from:'gradle文件path',即可使用该功能
        apply from:'../repositories_upload.gradle'
    }
}

apply plugin: 'maven' //maven仓库上传插件
apply plugin: 'maven-publish' //maven仓库上传插件
configurations {
    deployerJars
}

repositories {
    mavenCentral()
}

// 判断版本是Release or Snapshots
def isReleaseBuild() {
    return !VERSION.contains("SNAPSHOT")
}

// 获取仓库url
def getRepositoryUrl() {
//    return isReleaseBuild() ? RELEASE_URL : SNAPSHOT_URL
    return Repository_Url
}

//配置上传信息,最重要的是这一段
//我们平时添加依赖的时候就是这样compile 'com.android.support:support-annotations:26.1.0'
//com.android.support 对应 GROUP_ID
//support-annotations 对应 ARTIFACT_ID
//26.1.0 对应 VERSION
//而getRepositoryUrl()获取的是你的Maven仓库URL,userName和password分别对应Maven仓库的用户名和密码,上传到Maven仓库之前需要有你自己Maven仓库,这一步大家google一下基本都会了,当然也可以发布到本地仓库
uploadArchives {
    repositories {
        mavenDeployer {
            pom.version = VERSION
            pom.groupId = GROUP_ID
            pom.artifactId = ARTIFACT_ID
            pom.packaging ='aar'
            repository(url: getRepositoryUrl()) {
                authentication(userName: Authentication_UserName, password: Authentication_Password)
            }
        }
    }
}

// type显示指定任务类型或任务, 这里指定要执行Javadoc这个task,这个task在gradle中已经定义
task androidJavadocs(type: Javadoc) {
    // 设置源码所在的位置
    source = android.sourceSets.main.java.sourceFiles
}

// 生成javadoc.jar
task androidJavadocsJar(type: Jar) {
    // 指定文档名称
    classifier = 'javadoc'
    from androidJavadocs.destinationDir
}

// 生成sources.jar
task androidSourcesJar(type: Jar) {
    classifier = 'sources'
    from android.sourceSets.main.java.sourceFiles
}

// 产生相关配置文件的任务
artifacts {
    archives androidSourcesJar
    archives androidJavadocsJar
}
  1. Project自带属性
    /**
     * The default project build file name.
     * 所以所有的Project都需要一个默认的build.gradle文件,Gradle默认从该文件读取配置信息
     */
    String DEFAULT_BUILD_FILE = "build.gradle";

    /**
     * The hierarchy separator for project and task path names.
     *路径分割符,如windows文件路径使用斜杠分割
     */
    String PATH_SEPARATOR = ":";

    /**
     * The default build directory name.
     * 默认的build输出文件夹,默认build产生的apk等产物在此目录
     */
    String DEFAULT_BUILD_DIR_NAME = "build";

    /**
     *  Project-wide Gradle settings.
     *  IDE (e.g. Android Studio) users:
     *  Gradle settings configured through the IDE *will override*
     *  any settings specified in this file.
     *  在此属性文件中可修改一些Gradle默认的属性,也可扩展属性
     */
    String GRADLE_PROPERTIES = "gradle.properties";

    /**
     * 以下三个属性基本上不会使用到
     */
    String SYSTEM_PROP_PREFIX = "systemProp";

    String DEFAULT_VERSION = "unspecified";

    String DEFAULT_STATUS = "release";
  1. 为Gradle扩展属性
    Gradle自带的默认属性肯定是无法满足开发需求的,我们可以为Gradle扩展各种各样的属性来管理整个项目
//先来看build.gradle中常见的一部分配置,此处只做演示删掉大部分部分配置

apply plugin: 'com.android.application'
android {
    compileSdkVersion 26
}
dependencies {
    implementation 'com.android.support:appcompat-v7:26.1.0'
}

//前面说过build.gradle在配置阶段会被解析成Project字节码,所以在这里的配置实际上是在Project类中编写代码,而不仅仅是配置。
//写代码的话,里面的一些写死的魔法值如编译sdk版本26,还有写死的字符串appcompat-v7依赖com.android.support:appcompat-v7:26.1.0都是不符合编码风格的
//尤其是许多依赖的版本号都是一样的,如各种support包的版本我们可能使用一样的,写死我们修改就需要修改多个地方,或者说多个module中都需要使用的属性,在每个module中写死更是如此
//所以我们可以像平时开发一样,定义一些属性,并在配置的时候使用定义好的属性
//直接定义属性
apply plugin: 'com.android.application'

def mCompileSdkVersion = 26
def libSupportV7 = 'com.android.support:appcompat-v7:26.1.0'
android {
    compileSdkVersion mCompileSdkVersion
}
dependencies {
   implementation libSupportV7
}
//使用ext闭包扩展属性
apply plugin: 'com.android.application'
ext{
   compileSdkVersion = 26
   libSupportV7 = 'com.android.support:appcompat-v7:26.1.0' 
}
android {
    compileSdkVersion this.compileSdkVersion
}
dependencies {
   implementation this.libSupportV7
}

//直接定义属性和使用ext闭包扩展属性没有本质的区别

//但当定义的属性是所有的module都需要使用的时候,使用ext的优势就可以提现出来了,我们可以在根工程中使用ext扩展这些属性,然后在子工程中利用前面提到的rootProject来使用这些属性
//定义在根工程
ext{
   compileSdkVersion = 26
   libSupportV7 = 'com.android.support:appcompat-v7:26.1.0' 
}

//在子工程中使用
apply plugin: 'com.android.application'
android {
    compileSdkVersion this.rootProject.compileSdkVersion
}
dependencies {
   implementation this.rootProject.libSupportV7
}

//注意此处也可以省略掉rootProject,直接像上面使用this调用,因为在Gradle中,根工程定义的属性会被子工程继承
//一个项目可能有很多module,需要有很多的依赖和其它配置,所有的属性都写在根工程中可能导致根工程的build.gradle文件内容异常的多,不容易查找
//可以像上传library到Maven一样,将这些属性都定义到一个单独的文件中,然后在根工程中引入即可。

//根目录中新建commom_properties.gradle文件(定义两个map)
ext{
    android     =  [compileSdkVersion:26,
                    buildToolsVersion:'26.0.2']
               
    dependencies = [libSupportV7:'com.android.support:appcompat-v7:26.1.0' ,
                   libConstraintLayout:'com.android.support.constraint:constraint-layout:1.1.3']
}

//在根工程中引入
apply from:this.file('commom_properties.gradle') //file方法从当前project目录中查找文件

//在子工程中使用(ext中定义了各种map,直接使用key访问值)
apply plugin: 'com.android.application'
android {
    compileSdkVersion rootProject.ext.android.compileSdkVersion
    buildToolsVersion rootProject.ext.android.buildToolsVersion
}
dependencies {
   implementation rootProject.ext.dependencies.libSupportV7
   implementation rootProject.ext.dependencies.libConstraintLayout
}
//在gradle.properties文件中修改属性
//Android Studio 3.0会在debug apk的manifest文件application标签里自动添加
//android:testOnly="true"属性,导致IDE中run跑出的apk在大部分手机上只能用
//adb install -t <apk>来安装,在oppo手机上甚至安装不了
//解决办法:在gradle.properties文件中修改该属性

android.injected.testOnly=false //修改testOnly属性为false


//添加属性,gradle.properties除了可以修改已有属性之外,还可添加属性,全局可用
//在gradle.properties文件中添加
mApplicationId = wen.github.ecommerce
mVersionCode = 1
mCompileSdkVersion = 26

//在工程中使用
apply plugin: 'com.android.application'
android {
   compileSdkVersion mCompileSdkVersion
   defaultConfig{
       applicationId mApplicationId
       versionCode mVersionCode.toInteger()
   }
}

  1. 路径获取相关API
//获取根工程的路径
println getRootDir().absolutePath
//获取build目录路径
println getBuildDir().absolutePath
//获取当前工程路径
println getProjectDir().absolutePath
获取文件路径.png
  1. 文件操作相关API
//查找文件

//在根工程中
println getFileContent('gradle.properties') //输出gradle.properties的内容

def getFileContent(String path) {
    try {
        def file = file(path)
        return file.text
    } catch (GradleException e) { //file()方法在当前project目录下找,找不到会报FileNotFound异常
        println "File $path not found"
    }
    return null
}

//files()方法,接收多个路径参数,基于当前project工程查找多个文件,返回一个collection,使用与file()方法一致
gradle.properties内容.png
//文件的拷贝,讲解Groovy语法之文件操作中通过读取文件中的内容,然后写入到目标文件实现
//Gradle提供了更加简便的方法:copy()

//app module的build.gradle文件中
copy {
    from file('build/outputs/') //可以是文件也可以是目录
    into getRootProject().getBuildDir().path + '/rootOutputs/' //目标路径
    include '**/*.apk'   //选择要复制的文件
    include '**/*.json' //选择要复制的文件
    exclude { detail -> detail.file.name.contains('json') } //排除
    rename { 'aaa.apk'} //重命名
}

//将生成的outputs文件目录整个拷贝到根工程的build目录下的rootOutputs文件夹中
//文件树的遍历
fileTree("src/main/java") {//基于基准目录创建文件树
    FileTree fileTree ->
        fileTree.visit {//访问文件树的元素
            FileTreeElement element ->
                println element.file.name
        }
}


//输出结果
'''
wen
github
ecommerce
ECApplication.java
MainActivity.java

'''

//Android项目的根工程的build.gradle中的buildscript块相信大家都见过,新建的项目的时候会自动为我们配置一些东西

//根工程的build.gradle中
buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'
        
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

//为什么这样写不知道大家有没有考虑过,里面又能配置些什么
//buildscript块实际上就是Project.buildscript()方法相信大家都可以理解,点进方法的源码可以看到这个闭包接收一个ScriptHandler类型的参数,那么能够配置的东西就是ScriptHandler类里面提供的给我们的,主要有两个配置方法repositories和dependencies。
//这个buildscript块写完整实际上是下面这样,至于为什么可以去掉scriptHandler是因为闭包中的delegate就是去掉的scriptHandler,在闭包中只要this,owner,delegate中有的方法都是可以调用的,区别是调用的顺序根据闭包委托策略决定(可以到Groovy语法中讲解闭包的this,owner,delegate部分了解)。

buildscript{ ScriptHandler scriptHandler ->
    //配置工程的仓库地址
    scriptHandler.repositories {}
    
    //配置工程的“插件”依赖地址
    scriptHandler.dependencies {}
}

//同样的点进repositories和dependencies方法的源码可查看这两个方法接收的参数以及可配置的内容

buildscript { ScriptHandler scriptHandler ->
    //配置工程的仓库地址
    scriptHandler.repositories { RepositoryHandler repositoryHandler ->
        //一个项目可以有好几个库. Gradle 会根据依赖定义的顺序在各个库里寻找它们。在第一个库里找到了就不会再在第二个库里找它了
        //所以下面都是可选的,我们项目添加的那些依赖库存放在哪个仓库就需要在这配置,Gradle才能帮我们找到这些依赖库并下载下来
        repositoryHandler.google()  //google仓库
        repositoryHandler.jcenter()  //jcenter库
        repositoryHandler.mavenCentral() //maven中心库
        repositoryHandler.mavenLocal() //本地maven库
        repositoryHandler.maven { url "https://maven.google.com" } //配置maven私有仓库
        repositoryHandler.maven { url "https://jitpack.io" } //可以写多个(可理解为多次调用该方法)
        repositoryHandler.maven {
            name '公司名称'
            url "公司私有的maven仓库地址"
            credentials { //配置仓库用户名和密码
                username = 'myName'
                password = 'myPassword'
            }
        }
        repositoryHandler.ivy {} //ivy仓库
        flatDir { dirs '../${项目名称}/libs' } // aar等引入 ,这里只是声明了存放aar的路径,还需要在使用到aar的module的build.gradle的dependencies中引入对应的aar( implementation(name: 'aar名字', ext: 'aar')  )
        //注意如果是library类型的module中引入了aar,除了在该library module中引入,依赖了这个library的module中也需要再次引入
    }

    //配置工程的“插件”依赖地址,需要注意在这是scriptHandler的dependencies方法,而Project也有一个dependencies方法(即app module中的dependencies块)。
    //两者区别:我们的应用程序的开发都会引入各种第三方的依赖库,这些依赖库在Project的dependencies方法中配置,
    //而Gradle实际上也是一个编程框架,那么在开发Gradle工程时也需要一些第三方的插件,这些插件就在scriptHandler的dependencies方法中配置
    scriptHandler.dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1' //引入这个gradle之后就可以将我们的工程指定为android类型module或者是library类型module
        classpath 'com.tencent.tinker:tinker-patch-gradle-plugin:1.9.1' //腾讯热修复插件,引入热修复补丁包生成插件工具
    }
}


//引用一个外部依赖需要使用 group, name 和 version 属性. 如下
dependencies {
    compile group: 'com.android.support', name: 'appcompat-v7', version: '26.1.0'
}

//有一种简写形式,只使用一串字符串 "group:name:version".如下,一般都是使用这种方式进行配置
dependencies {
    compile 'com.android.support:appcompat-v7:26.1.0'
}


//app工程的build.gradle中
dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs') //添加文件树依赖(本地的jar包),另外还可以添加file和files依赖,取决于添加的依赖是文件树还是单一文件或者多个文件
    implementation 'com.android.support.constraint:constraint-layout:1.1.3' //依赖远程jar包
    testImplementation 'junit:junit:4.12' //单元测试
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' //android中的测试
    implementation project(':ec')  //依赖源码库工程
    implementation project(':core') //依赖源码库工程
    annotationProcessor project(':compiler') //编译期注解的依赖
    //optional, help to generate the final application 
    provided('com.tencent.tinker:tinker-android-anno:1.9.1') //为了在编译的时候生成一个Application类,使用时不需要
    //tinker's main Android lib
    compile('com.tencent.tinker:tinker-android-lib:1.9.1')
    debugCompile 'com.amitshekhar.android:debug-db:1.0.0' //数据库debug
}

  1. compile
    参与编译和打包,使用compile方式依赖的第三方库中所有的类,资源文件等都会被打包到项目的最终产物中(apk,jar,aar等)
  2. provided
    编译占位,只参与编译的过程,不会打包到apk,适用于只在编译的时候起作用在运行期不需要使用的库,比如Tinker的tinker-android-anno,只为了在编译的时候生成需要的Application类,运行时就不再需要这个工具了,另外适用于当前工程需要引入一个主工程已经引入的库时,比如在library module中作为工具类,先用provided占位,编译通过,在使用的时候由用户根据需求,如果要使用该功能的话,再引入一次。
  3. apk
    只在生成apk的时候参与打包,编译时不会参与
  4. testCompile
    参与单元测试代码的编译以及最终打包测试apk
  5. debugCompile
    参与debug模式的编译和最终的debug apk打包
  6. releaseCompile
    参与release模式的编译和最终的release apk打包
  1. implementation
    对于使用了该命令编译的依赖,对该项目有依赖的项目将无法访问到使用该命令编译的依赖中的任何程序,即将该依赖隐藏在内部,而不对外部公开(非传递依赖)
    简单来说,从Android Studio 3.X开始,依赖首先应该设置为implement,如果没有错那就用implement,如果有错,那么使用api指令,这样会使编译速度有所增快。假设有A,B,C三个module,B,C依赖于A,那么在A中使用implementation依赖的库,在B,C中无法直接使用,在A中使
    用implementation依赖的库更改时,只在A中重新编译,在B,C中不需要编译所以会加快编译速度
  2. api
    同2.x版本的compile,参与编译和打包,使用compile方式依赖的第三方库中所有的类,资源文件等都会被打包到项目的最终产物中(apk,jar,aar等)
  3. compileOnly
    同2.x版本的provided,编译占位,只参与编译的过程,不会打包到apk,适用于只在编译的时候起作用在运行期不需要使用的库,另外适用于当前工程需要引入一个主工程已经引入的库时
  4. runtimeOnly
    同2.x版本的apk,只在生成apk的时候参与打包,编译时不会参与
  5. testImplementation
    同2.x版本的testCompile,参与单元测试代码的编译以及最终打包测试apk
  6. debugImplementation
    同2.x版本的debugCompile,参与debug模式的编译和最终的debug apk打包
  7. releaseImplementation
    同2.x版本的releaseCompile,参与release模式的编译和最终的release apk打包
  8. annotationProcessor
    编译注解依赖,只参与编译过程,不会打包到apk,目的是在编译时动态生成代码。
//版本依赖冲突解决
//1. 强制依赖指定版本
configurations.all {
  resolutionStrategy.force 'com.android.support:support-annotations:26.1.0'
}

//完整写法,排除multidex
 configurations.all {
        resolutionStrategy.eachDependency { DependencyResolveDetails details ->
            def requested = details.requested
            if (requested.group == 'com.android.support') {
                if (!requested.name.startsWith("multidex")) {
                    details.useVersion '26.1.0'
                }
            }
        }
    }


//2. 排除
implementation ('com.squareup.retrofit2:adapter-rxjava:2.1.0'){
        exclude group: 'io.reactivex' //排除指定包下的所有库
    }
implementation 'io.reactivex.rxjava2:rxjava:2.0.6'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'

//另外如果冲突的是其中的一个module
implementation ('com.squareup.retrofit2:adapter-rxjava:2.1.0'){
        exclude module: '冲突的module name'  //排除指定module
    }

//传递依赖
//假设有工程A,B,C,工程A依赖了B,而B又依赖了C,这个时候就构成了传递依赖
//而在开发中一般是不允许A直接使用C的,因为可能B升级了之后,不需要再依赖C了,如果在A中直接使用了工程C中的东西的话,就会导致各种编译错误,因为B已经没有依赖C了,在A中使用的C中的东西都已经找不到了
conpile ('com.squareup.retrofit2:adapter-rxjava:2.1.0'){
        transitive false //禁止传递依赖
    }
//在Gradle中默认transitive就是false,另外在Android Studio 2.x 版本和3.x依赖指令的区别部分,我们也可以知道,使用implementation,本身也是非传递依赖,在B中使用implementation依赖C,A再依赖B,本身C中的API那些就无法在A中直接使用

  1. 打印出该模块所有的依赖树信息:gradlew -q <模块名>:dependencies
  2. 将项目切换至Project面板,在External Libraries目录下查看
  3. 项目的Gradle Projects面板中运行 Tasks->android->androidDependencies
// 使用exec()方法执行外部命令,对于其它的javaexec方法,只要配置好环境变量,实际上通过exec方法也是可以执行的

task(name:'apkcopy'){ //创建一个名为apkcopy的task
    doLast{  //在task的最后执行
        def sourcePath = this.buildDir.path+'/outputs/apk'
        def destinationPath = '/Users/wen/apks' //要存放的文件路径
        def command = "mv -f $sourcePath $destinationPath" //命令行
        exec{
            try {
                executable 'bash'  //要使用的可执行文件的名称
                args '-c',command  //要执行的命令的参数。默认为空列表
                println 'the command is execute success'
            } catch (GradleException e) {
                println 'the command is execute failed'
            }
        }
    }
}


  1. 什么是Task
    一个Task表示构建的单个原子动作。构建工作就是由各种Task构成的。我们也可以定义各种各样的Task。在Gradle写的其它脚本都会在配置阶段被执行(利用生命周期的监听除外),并且执行的顺序不确定,而利用Task可以在执行阶段执行我们需要的逻辑,可并为Task指定执行顺序。除此之外我们还可以为我们自己编写的Task指定类型来让Task具有更强的功能。
  1. Task定义和配置
//定义

//1. 通过task方法创建 
task getDateTask {
    println new Date().dateString //在终端输入gradlew getDateTask 执行Task输出:18-10-16
}


//2.通过TaskContainer创建
this.tasks.create(name: 'getDateTask2') {
    println "getDateTask2 date: ${new Date().dateString}" 
}


//TaskContainer是用来管理一个Project中所有的Task的,提供创建和查找task功能。查找对应task最好是在配置阶段之后,此时所有的task都配置好了。否则可能出现找不到对应的task
task执行结果.png
//配置
//1. 创建的时候直接配置
task getDateTask(group:'learn',description:'task study') {
    println new Date().dateString
}

task clean(type: Delete) { //配置类型
    delete rootProject.buildDir
}

//2. 方法配置
task getDateTask2 {
    setGroup('learn') //配置组
    setDescription('task study') //配置描述
    println new Date().dateString
}

    String TASK_NAME = "name"; //名字

    String TASK_DESCRIPTION = "description"; //描述

    String TASK_GROUP = "group"; //所属组

    String TASK_TYPE = "type";  //类型

    String TASK_DEPENDS_ON = "dependsOn";  //task依赖(依赖于其它task)

    String TASK_OVERWRITE = "overwrite"; //是否重写其它task 默认false

    String TASK_ACTION = "action"; //配置task相关逻辑
task getDateTask(group: 'learn', description: 'task study') {
    println new Date().dateString
    //在闭包中调用
    doFirst { println "the group is:$group" }
}
//在闭包外调用
getDateTask.doFirst { println "the description is:$description" }
dofist.png
task getDateTask(group: 'learn', description: 'task study') {
    println new Date().dateString
    doLast {println "the description2 is:$description"}
    //在闭包中调用
    doFirst { println "the group1 is:$group" }
    doFirst { println "the group2 is:$group" }
}
//在闭包外调用
getDateTask.doFirst { println "the description1 is:$description" }
doFirst和doLast的区别.png
//统计项目构建时间
def startBuildTime, endBuildTime
this.afterEvaluate { Project project ->
    //在配置阶段之后(所有的task都被配置好,构建了task拓扑图)查找task 防止找不到对应task
    def preBuildTask = project.tasks.getByName('preBuild') //构建最先执行preBuild task
    def buildTask = project.tasks.getByName('build') //构建最终执行build task

    preBuildTask.doFirst {
        startBuildTime = System.currentTimeMillis()
        println "build start,current time is $startBuildTime"
    }

    buildTask.doLast {
        endBuildTime = System.currentTimeMillis()
        println "build end,current time is $endBuildTime"

        println "The build took ${(endBuildTime - startBuildTime) / 1000} seconds"
    }
}

项目构建时间.png
  1. dependsOn配置依赖决定执行顺序

    在前面Task所有可配置内容中有一个dependsOn属性,这个属性是用于配置Task依赖的,执行一个task的时候,这个task所依赖的task会先被执行,但是执行它依赖的task对它是不会造成影响的
task taskA {
    doLast {
        println 'taskA run'
    }
}

task taskB {
    doLast {
        println 'taskB run'
    }
}

task taskC(dependsOn:[taskA,taskB]) { //为taskC配置依赖
    doLast {
        println 'taskC run'
    }
}
//动态的指定依赖

task taskA(group:'gradle'){
    doLast {
        println 'taskA run'
    }
}

task taskB(group:'gradle'){
    doLast {
        println 'taskB run'
    }
}

task taskC(group:'java'){
    doLast {
        println 'taskC run'
    }
}

task taskD { //动态的指定task依赖
    dependsOn this.tasks.findAll {task -> task.group == 'gradle' }
    doLast {
        println 'taskD run'
    }
}
动态指定依赖.png
  1. mustRunAfter()、shouldRunAfter() API指定执行顺序

    需要注意的是,使用mustRunAfter和shouldRunAfter是在两个task都会执行的情境下会按照该指定顺序执行,调用了该Api指定依赖执行顺序的两个task并没有依赖关系。单独执行其中一个对另外的任务并不会有影响,但如果mustRunAfter和task依赖之间发生了冲突,那么执行时将会报错
task taskA{
    doLast{
        println 'taskA run'
    }
}

task taskB{
    doLast{
        println 'taskB run'
    }
}

task taskC{
    doLast{
        println 'taskC run'
    }
}

taskB.mustRunAfter(taskC)
taskA.mustRunAfter(taskB)
API指定task执行排序.png 非依赖执行.png
//假设Gradle构建的生命周期中有taskA和taskB,taskA执行之后会执行taskB,,那么需要将myTask,插入taskA和taskB中间

//以下是伪代码
project.afterEvaluate{
  
    //1.找到taskA和taskB
    //可以通过TaskContainer的findTaskByName方法或其它方式获取到响应的task
    //假设已找到taskA和taskB
    
    //2.将myTask插入到taskA之后
    myTask.mustRunAfter taskA
    
   //3. 将myTask插入到taskB之前
   taskB.dependsOn myTask
}

  1. finalizedBy

    如果需要实现执行某一个task之后必须执行另一个task,用上面的方法是没办法的,此时可以利用finalizedBy API
task taskA{
    doLast{
        println 'taskA run'
    }
}

task taskB{
    doLast{
        println 'taskB run'
    }
}

//执行完taskA之后必须执行taskB
taskA.finalizedBy taskB
执行taskA后taskB跟着执行.png
  /**
     * <p>Returns the inputs of this task.</p>
     *
     * @return The inputs. Never returns null.
     */
    @Internal
    TaskInputs getInputs();

    /**
     * <p>Returns the outputs of this task.</p>
     *
     * @return The outputs. Never returns null.
     */
    @Internal
    TaskOutputs getOutputs();
  /**
     * 返回所有task的输入文件
     *
     * @return 返回输入文件,如果task没有输入文件返回一个空的文件集合
     */
    FileCollection getFiles();

    /**
     * 指定该task的输入文件(多个文件)
     *
     * @param 输入文件路径
     * @return 返回property builder以进一步配置属性
     */
    TaskInputFilePropertyBuilder files(Object... paths);

    /**
     *指定该task的输入文件(一个文件)
     *
     * @param 输入文件路径. 
     * @return 返回property builder以进一步配置属性
     */
    TaskInputFilePropertyBuilder file(Object path);

    /**
     * 为该task注册一个输入目录。在给定目录下找到的所有文件都被视为输入文件
     *
     * @return 返回property builder以进一步配置属性
     */
    TaskInputFilePropertyBuilder dir(Object dirPath);

    /**
     * 返回该task的输入属性集。
     *
     * @return The properties.
     */
    Map<String, Object> getProperties();

    /**
     * 为该task注册一个输入属性。该属性值在task执行时持久化,并task的后
     * 调用的属性值进行比较,以确定task是否最新。
     *
     *属性的给定值必须是可序列化的,并且应该提供一个有效的equal()方法
     *
     *也可指定一个闭包作为属性的值,在这种情况下会执行该闭包用于确定属性的值
     *
     * @param name The name of the property. Must not be null.
     * @param value The value for the property. Can be null.
     */
    TaskInputs property(String name, Object value);

    /**
     * 为该Task注册一个输入属性集合
     *
     * @param properties The properties.
     */
    TaskInputs properties(Map<String, ?> properties);
    
    //注意TaskInputFilePropertyBuilder继承于TaskInputs,用于描述包含零个或多个文件的task的输入属性。
  /**
     * 返回该Task的所有输出文件
     *
     * @return The output files. Returns an empty collection if this task has no output files.
     */
    FileCollection getFiles();

    /**
     * 指定该Task的输出是文件(多个)
     *
     *如果是一个map,key必须是有效的java标识符,否则task输出缓存会被禁用
     *
     * @param paths The output files.
     *
     * @see CacheableTask
     */
    TaskOutputFilePropertyBuilder files(Object... paths);

    /**
     * 该Task输出是文件目录
     *
     *
     * @param paths The output files.
     *
     * @see CacheableTask
     *
     */
    TaskOutputFilePropertyBuilder dirs(Object... paths);

    /**
     * 指定该Task的输出是文件(单个)
     *
     */
    TaskOutputFilePropertyBuilder file(Object path);

    /**
     * task输出文件是dir
     */
    TaskOutputFilePropertyBuilder dir(Object path);
    
    //注意TaskOutputFilePropertyBuilder继承于TaskOutputs,用于描述包含零个或多个文件的task的输出属性。
  //在AS中,gradle默认去加载jniLibs目录中的 *.so 文件
    sourceSets { //soutceSet方法重定位资源文件的位置
        main {//jniLibs在main目录下,所以需要调用main方法
            jniLibs.srcDirs = ['libs'] //修改到libs目录中加载*.so文件
        }
    }
    
 //修改res的目录,有时候为了便于管理,单独的一个模块的资源文件都放在一个单独的目录中,想要gradle能够正确识别到这些文件,需要而外添加进来   
     sourceSets {
        main {
        //指定从多个路径加载资源文件
            res.srcDirs = [  
                    'src/main/res/chat-res',
                    'src/main/res'
            ]
        }
    }
上一篇 下一篇

猜你喜欢

热点阅读