Gradle基础与使用

1.gradle执行流程

gradle在使用中有很多,tinker就是使用修改gradle打包的方式来实现热补丁的

(1)-》初始化阶段(解析工程所有的Project,构建Project对于的project对象)

-》配置阶段(解析所有project中的task,同时构建好所有task的拓扑图,具体来说这是一个有向无环图,每个节点代表一个task方向表示依赖)

Gradle基础与使用_第1张图片

-》执行阶段(执行具体的task,在执行task时会先调用该task依赖的task,也就是拓扑图上的所有task),

对于gradle项目,最先执行的就是build.gradle文件,初始化阶段会去调用setting.gradle.

我们可以在setting.gradle中修改

Gradle基础与使用_第2张图片

/*配置阶段开始前回调*/
this.beforeEvaluate {

}
/*配置阶段完成后回调*/
this.afterEvaluate {
    println("配置完毕")
}
/*gradle执行完毕回调*/
this.gradle.buildFinished {
    println("执行完毕")
}

Gradle基础与使用_第3张图片

然后我们随便执行一个gradle命令就可以看到,注意到这里gradle命令是gradlew,常用的命令有clean,tasks(列出所有task)等,build,projects这些都是task,当执行一个task时,会执行所有和这个task依赖的task

E:\vode\blue>gradlew clean
初始化开始
配置完毕
:clean

BUILD SUCCESSFUL

Total time: 2.249 secs
执行完毕

 

 

gradle中其他的生命周期监听方法还有

/*等同于beforeEvaluate*/
this.gradle.beforeProject {

}
/*等同于afterEvaluate*/
this.gradle.afterProject {

}
this.gradle.addBuildListener(new BuildListener() {
    @Override
    void buildStarted(Gradle gradle) {

    }

    @Override
    void settingsEvaluated(Settings settings) {

    }

    @Override
    void projectsLoaded(Gradle gradle) {

    }

    @Override
    void projectsEvaluated(Gradle gradle) {

    }

    @Override
    void buildFinished(BuildResult buildResult) {

    }
})

this.gradle.addListener()

 

 

 

2.Project详解

(1)我们先在命令行输入这个命令

E:\vode\blue>gradlew projects

可以看到这样的结构,其中blue是根模块,testModel是子模块,可以看到他们的层级关系,他们都是一个gradle中的Project,有与该项目对应的build.gradle文件,如果把这个build.gradle删除掉,这个就会变成一个普通的文件夹,而不再是一个model。

Gradle基础与使用_第4张图片

 

Gradle基础与使用_第5张图片

(2)通过gradle代码获取上面的结果,那就是通过this.getAllprojects() API 来获取所有的Project.同时可以使用this.getSubprojects()来获取当前Project的子Project.

this.getParent()来获取当前Project的父Project.

this.getRootProject()来获取根Project

gradle是以树形结构来管理Project的,所以getParent和getSubprojects()有可能是空的。

def getPros(){
    println('...............................')
    println("自定义实现获取项目Project")
    println('...................................')

    this.getAllprojects().eachWithIndex {
        Project pro,int index->
            if (index==0){
                println "root"+pro.getName()
            }else {
                println 'project'+pro.name
            }
    }
//this.getSubprojects()
//this.getParent()
}

getPros()

我们在build文件中定义方法并调用,可以看到如下输出

...............................
自定义实现获取项目Project
...................................
roottestModel
:testModel:help

(3)Project的project()方法,进入Project类,可以看到

Gradle基础与使用_第6张图片

第一个参数是project路径,第二个是一个闭包,所以在根build里面定义下面方法

project('testModel'){
    Project pro ->
        println(pro.name)
}

我们甚至可以这样,去配置子工程

project('testModel'){
    Project pro ->
        /*获取到project就可以调用该project的所有方法*/

        /*比如对子工程进行配置*/
        group 'vode'
        version '2.0'
        apply plugin: 'java'
        /*添加groovy依赖*/
        apply plugin: 'groovy'
        dependencies {
            testCompile group: 'junit', name: 'junit', version: '4.12'
        }
}

(4)配置当前节点下所有的project

/*配置所有project包括当前和子project*/
allprojects {

    Project pro ->
        group 'vode'
        version '1.2.0-release'
}

println project('testModel').version

在log里可以看到,配置生效

Gradle基础与使用_第7张图片

 

(5)只配置子project,还可以判断project类型来具体配置

/*只包括子project*/
subprojects{
    Project pro ->
        if (pro.plugins.hasPlugin('com.android.library')){
            apply from:'../config.gradle'
        }
}

(6)Project的属性,下图是gradle自带的全部属性

Gradle基础与使用_第8张图片

自定义属性的话,可以这样做

def mVersion=26
ext{
    targetVersion=26
}

println(mVersion)
println(this.targetVersion)

可以在log中看到

Gradle基础与使用_第9张图片

 

/*为所以的子工程配置属性*/
subprojects{
    /*这里相当于在每一个project配置一个ext语句块*/
    ext{
        version=1.8
    }
}

这样会有重复代码,我们优化,在根build中穿件ext语句块,定义一个属性,然后再在子project通过获取根project调用属性

ext{
    sdkversion=1.8
}
sourceCompatibility = this.rootProject.sdkversion
//sourceCompatibility = this.rootProject.ext.sdkversion

我们还可以,把所有的配置都写在一个gradle文件中,并且通过apply from: 来调用

Gradle基础与使用_第10张图片

apply from:this.file('config.gradle')

(7)可以通过gradle.properties文件来定义属性,如果没有看到这个文件,可以直接在根目录下创建这个文件,这个在Project的属性中已经定义了路径。注意这个文件里只能通过键值对这种来定义,而且在获取值得时候,不能直接使用,必须通过toXXX()转化为特定类型。

Gradle基础与使用_第11张图片

我们在setting.gradle中添加如下代码

if (hasProperty('isDebug')? isDebug.toBoolean():false){
    include 'testModel'
    println('test model')
}

println(sdkVersion.toString())

Gradle基础与使用_第12张图片

编译一下,可以看到如下log

Gradle基础与使用_第13张图片

 

3.Project中的文件操作

(1)常用获取文件的API

println getRootDir().absolutePath
println getBuildDir().absolutePath
println getProjectDir().absolutePath

E:\vode\blue
E:\vode\blue\build
E:\vode\blue

(2)通过file()方法来获取文件,这个也是Project的一个方法

def getContext(String path){
    try {
        //def file = new File(path)
        /*相对于当前project开始查找文件*/
        def file = file(path)
        return file.text
    }catch (Exception e){

    }
}
println getContext("setting.gradle")

 

(3)文件拷贝,也是使用Project类的方法,copy参数是闭包,这个不仅仅能拷贝文件,也可以直接拷贝文件夹。

copy {
    from file('copyText.txt')
    into getRootProject().getBuildDir()
}

将testModel 里面的copyTest文件拷贝到了根目录的build文件夹下面,如下

Gradle基础与使用_第14张图片

copy {
    from file('copyText.txt')
    into getRootProject().getBuildDir()

    exclude {
        /*筛选条件*/
        
    }
    rename {
        /*重命名*/
    }
}

(4)对文件树进行遍历

/*文件树遍历*/
fileTree('src'){
    FileTree tree->
        tree.visit {
            FileTreeElement element->
                println(element.file.name)
                copy {
                    from(element.file)
                    into(getRootProject().getBuildDir().path+'/test/')
                }
        }
}

Gradle基础与使用_第15张图片

 

(5)buildscript的使用,该方法参数是个闭包,闭包的参数是ScriptHandler ,再看ScriptHandler有两个主要方法

repositories { /*工程仓库地址*/}和handler.dependencies { /*工程插件地址*/ }

Gradle基础与使用_第16张图片

buildscript{
    ScriptHandler handler->
        handler.repositories {
            /*工程仓库地址*/
            RepositoryHandler repositories->
                /*各个支持仓库*/
                repositories.jcenter()
                /*这里配置一下本地maven地址*/
                repositories.maven {
                    /*仓库名*/
                    name 'personal_maven'
                    /*仓库地址*/
                    url 'http://localhost/path/'
                    /*如果需要配置用户信息,可以在这里*/
                    credentials{
                        username ='vode'
                        password ='123456'
                    }
                }
        }
        handler.dependencies {
            /*工程插件地址,也就是gradle本身对第三方的依赖,和项目对第三方的依赖是有区别的*/
            classpath 'com.android.tools.build:gradle:2.2.2'
        }
}

在repositories 可以看到可以配置各个仓库,进入RepositoryHandler 可以看到支持的类库,其中flatDir就是文件,加载libs里的jar包。

public interface RepositoryHandler extends ArtifactRepositoryContainer {
    FlatDirectoryArtifactRepository flatDir(Map var1);

    FlatDirectoryArtifactRepository flatDir(Closure var1);

    FlatDirectoryArtifactRepository flatDir(Action var1);

    MavenArtifactRepository jcenter(Action var1);

    MavenArtifactRepository jcenter();

    MavenArtifactRepository mavenCentral(Map var1);

    MavenArtifactRepository mavenCentral();

    MavenArtifactRepository mavenLocal();

    MavenArtifactRepository maven(Closure var1);

    MavenArtifactRepository maven(Action var1);

    IvyArtifactRepository ivy(Closure var1);

    IvyArtifactRepository ivy(Action var1);
}
public interface DependencyHandler {
    Dependency add(String var1, Object var2);

    Dependency add(String var1, Object var2, Closure var3);

    Dependency create(Object var1);

    Dependency create(Object var1, Closure var2);

    Dependency module(Object var1);

    Dependency module(Object var1, Closure var2);

    Dependency project(Map var1);

    Dependency gradleApi();

    @Incubating
    Dependency gradleTestKit();

    Dependency localGroovy();

    @Incubating
    ComponentMetadataHandler getComponents();

    @Incubating
    void components(Action var1);

    @Incubating
    ComponentModuleMetadataHandler getModules();

    @Incubating
    void modules(Action var1);

    @Incubating
    ArtifactResolutionQuery createArtifactResolutionQuery();

    @Incubating
    AttributesSchema attributesSchema(Action var1);

    @Incubating
    AttributesSchema getAttributesSchema();

    @Incubating
    void registerTransform(Action var1);
}

 

(6)为具体模块提供依赖

/*为程序提供依赖*/
dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.3.11'
    testCompile group: 'junit', name: 'junit', version: '4.12'

    //compile file('xxxx.jar')
    /*文件夹情况*/
    compile fileTree(includes: ['*.jar'],dir: 'libs')
    /*子工程*/
    compile project(':testModel')
    /*第三方依赖*/
    compile 'com.zhy:okhttputils:2.3.8'
}

依赖冲突,当我们引入很多的依赖,就可能导致多次引入同一个依赖,造成依赖冲突,其实,多次依赖是不会报错的,但是如果多次依赖的项目的版本不同,这样才会导致依赖冲突。

compile('xxx'){
    /*排除一个模块*/
    exclude module: '包中有冲突的模块'
    /*排除一组模块*/
    exclude group: 'com.android.support'
}

传递依赖的问题

compile('xxx'){
    /*排除一个模块*/
    exclude module: '包中有冲突的模块'
    /*排除一组模块*/
    exclude group: 'com.android.support'

    /*是否支持传递依赖,true就是支持传递依赖,就是A->B,B->C,A就是传递依赖C,不建议使用传递依赖,当我们需要依赖B时,自己去依赖C就可以了*/
    transitive false
}

站位编译问题

/*站位编译,编译时会使用这个模块的资源,但是打包是不会把这个模块的内容不会打入包中(这样如果不是确定后期会在别的地方把需要的模块打到包里面,是会报错的,或者这个模块只是在编译时运行也可以),compile就会打入包中,所以只在编译时期运行就可以使用provided,像是Android中butterKnife依赖注入*/
provided('xxx')

(7)Project使用外部命令

task(name:'apkcopy'){
    doLast{
        //在gradle的执行阶段执行

        def source='main/out'
        def destination='E:/vode/blue/test/'
        def command="mv -f ${source} ${destination}"

        /*javaexec {

        }*/
        exec {
            try{
                executable 'bash'
                args '-c' command
            }catch (Exception e){
                println(e.getMessage())
            }
        }

    }
}

4.Task使用

(1)创建一个task,这两种方法最终都是添加到TaskContainer管理类,进行管理。

task helloTask{
    println("hello task")
}

this.tasks.create(name:'helloTask2'){
    println( 'hello task2')
}

Gradle基础与使用_第17张图片

(2)配置task信息,比如这里配置了同样的分组group,就可以放在同一个组

/*配置task*/
task helloTask(group: 'vdoe',description:'task des'){
    println("hello task")
}

/*TaskContainer管理类,进行管理*/
this.tasks.create(name:'helloTask2'){

    setGroup('vode')
    setDescription('task des')
    println( 'hello task2')
}

其他的可配置信息,可以在源码里看到

Gradle基础与使用_第18张图片

 

(3)task的执行时间

task helloTask(group: 'vdoe',description:'task des'){
    println("hello task")
    doFirst{
        println "task "+group
    }

    doLast{

    }
}
helloTask.doFirst{
    println description
}

可以看到:helloTask后输出的,doFirst里的内容,所以这是在执行阶段运行的

前面的println("hello task")是在编译时执行的

Gradle基础与使用_第19张图片

(4)写一个计算编译时间功能

pro.tasks.each { Task task-> println task.name }通过这个可以看出,当前Project的所有task

Gradle基础与使用_第20张图片

def startT,endT

/*配置阶段结束*/
this.afterEvaluate {
    Project pro->



        /*preBuild task最早开始*/
        def preTask=pro.tasks.getByName('assemble')
        preTask.doFirst {
            startT=System.currentTimeMillis()
            println("start")
        }
        /*build task最后*/
        def buildTask=pro.tasks.getByName('build')
        buildTask.doLast {
            endT=System.currentTimeMillis()

            println(endT-startT)
            println("end")
        }
}

Gradle基础与使用_第21张图片

这里我们看到这个Project最早执行的task是compileJava task(其他的module可能会先执行,所以说是这个project),但是如果去写成这样,会爆出这样一个错误,我猜他应该是这个compileJava task自身对doFirst()进行了重载,导致我们这里我们重载的时候无法匹配,换成assemble task是没问题的。

Ambiguous method overloading for method java.lang
.Long#minus.

 

def preTask=pro.tasks.getByName('compileJava')
preTask.doFirst {
    startT=System.currentTimeMillis()
    println("start")
}

但是这个问题是为什么呢?在stack上看到一个这个

Now before Groovy 2.4 we had no support for the overloaded case. Which method was invoked was actually random. In other words, you had luck if it always called the right method for your cases. Since Groovy 2.4, Groovy supports overloaded setter, but this can then lead to cases, in which the setter overload causes exceptions like the one above. Something I consider good, since it shows something that could have caused mysterious errors in the past.

在Groovy 2.4之前,我们不支持重载案例。 调用哪种方法实际上是随机的。 换句话说,如果它总是为您的案例调用正确的方法,那么您很幸运。 从Groovy 2.4开始,Groovy支持重载setter,但这可能会导致setter重载引起异常,例如上面的异常。 我认为好的东西,因为它显示了过去可能导致神秘错误的东西。

但是我看了我的groovy 版本是2.4.10,so 这是为什么呢?然后我就发现是我傻逼了,这个错误是在后面相减时候的,是因为compileJava task的doFirst没有调用,这个startT没有赋值导致的。startT=0就没有问题了,然后是doFirst没有调用的原因。

Gradle基础与使用_第22张图片

 

在Android项目中最早是 task ‘prebuild’,最后是task 'build',执行顺序是这样的,通过这个可以计算build时长,下面是一个Android项目,在命令行gradlew build,看到,配置完成后,最开始的task就是preBuild,最后的task是build

Gradle基础与使用_第23张图片

(5)为task添加依赖

task task1{
    doLast{
        println('task1')
    }
}
task task2{
    doLast{
        println('task2')
    }
}
task task3(dependsOn:[task1,task2]){
    doLast{
        println('task3')
    }
}

我们写了三个task,其中task3依赖task1,task2,然后执行task3就可以看到,1和2都调用了,注意这里,task1和task2的执行顺序,是随机的。

Gradle基础与使用_第24张图片

这样添加task依赖也是一样的,

task task1{
    doLast{
        println('task1')
    }
}
task task2{
    doLast{
        println('task2')
    }
}
/*dependsOn:[task1,task2]*/
task task3(){
    doLast{
        println('task3')
    }
}

task3.dependsOn([task1,task2])

(6)条件添加task依赖,注意这样写这个gradle代码是有顺序的,其实是生命周期的原因而不是代码需要顺序,因为dependsOn方法是在生命周期方法外的,所以可能运行的时候,lib1,lib2,module1这几个task还没创建,导致找不到。

/*<< 相当于 doLast 已经deprecated*/
task lib1 {
    doLast{
        println('lib1')
    }
}

task lib2 {
    doLast{
        println('lib2')
    }

}

task module1{
    doLast{
        println('module1')
    }
}

/*dependsOn:[task1,task2]*/
task task3{
    dependsOn this.tasks.findAll {
        task->
            println(task.getName())
            return task.name.startsWith('lib')
    }
    doLast{
        println('task3')
    }
}

可以看到task3成功依赖了lib1,lib2

Gradle基础与使用_第25张图片

 

(7)添加依赖的例子,handleReleaseFile用于把book里的xml标签,解析到generate文件下,并为每个标签创建一个单独的文件,文件名用release-${year}.text来命名。

Gradle基础与使用_第26张图片

task handleReleaseFile{
    def srcFile=file('books')
    def desFile=new File(this.projectDir,'generate'+File.separator)

    if (!desFile.exists()){
        desFile.mkdirs()
    }
    doLast{
        println('start ........')
        def rel=new XmlParser().parse(srcFile)
        rel.movie.each{
            node->
                def des= node.description.text()

                def year=node.year.text()
                def dest=new File(desFile,"release-${year}.text")

                if(!dest.exists()){
                    dest.createNewFile()
                }
                dest.withWriter {
                    write->
                        write.write(
                                "des:  ${des}"
                        )
                }
        }
    }
}

task handleReleaseFileTest(dependsOn:handleReleaseFile){
    def dir=fileTree(this.projectDir.path+File.separator+'generate'+File.separator)

    doLast{
        dir.files.each{
            println(it.name)
        }
    }
}

运行task handleReleaseFileTest 可以看到,先运行了其依赖的task handleReleaseFile,然后再运行其本身

Gradle基础与使用_第27张图片

 

(8)task的输入和输出,就像函数传递参数一样和返回数据数一样,查看Task源码可以看到

/**
 * 

Returns the inputs of this task.

* * @return The inputs. Never returns null. */ @Internal TaskInputs getInputs(); /** *

Returns the outputs of this task.

* * @return The outputs. Never returns null. */ @Internal TaskOutputs getOutputs();

 

 

 

 

(9)

 

 

 

(10)

 

 

 

 

 

 

 

 

你可能感兴趣的:(gradle,Gradle)