Android Gradle的基本概念梳理

Posted 好人静

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Gradle的基本概念梳理相关的知识,希望对你有一定的参考价值。

目录

一 Gradle构建项目的三个阶段

1.Gradle构建项目之初始化阶段

(1)作用

(2)相关生命周期方法

2.Gradle构建项目之配置阶段

(1)作用

2)相关生命周期方法

3.Gradle构建项目之执行阶段

(1)作用

(2)相关生命周期方法

二 Gradle中的Project

1.Project中的Task

(1)定义Task

(2)创建Task的其他方式

(3)Action

(4)Task的执行顺序

2.Project中的Dependencies

(1)ConfigurationContainer

(2)DependencyHandler

(3) RepositoryHandler

(4)ArtifactHandler

三 Android Gradle中的buildSrc

四 猜测自定义Task在build.gradle中的执行问题

五 总结


前言

Android Gradle插件开发初次交手(一)中已经尝试了怎么自定义一个Gradle插件,但是里面涉及的一些概念还是要梳理下。另外我觉得可以用用Gradle插件做很多事情,想着去学习一下这方面的内容。

一 Gradle构建项目的三个阶段

Gradle是一个构建工具,就是将代码变成一个APK应用常见的其他构建工具如:ant、maven。在通常我们用android Studio新建的一个工程,通常在主目录下一个settings.gradle文件、一个build.gradle文件,在每个module下都有一个build.gradle文件。那么这些文件到底有什么作用呢?

一个Gradle在构建项目的时候需要经过三个阶段:

  • (1)初始化阶段:创建Project对象
  • (2)配置阶段:创建Project下的Task
  • (3)执行阶段:执行Task

当然Gradle提供了一些方法来监听Gradle构建项目的过程,提供这些Hook点供开发人员修改构建过程中的行为。但是该监听方法一定要在生命周期方法调用之前进行调用,否则会根本不会调用到对应的方法。对应上面说的三个阶段对应的不同方法的回调,如下图:

所以对于该Gradle构建过程的监听事件,需要放到settings.gradle文件中,代码如下:

//监听Gradle的生命周期需要放在settings.gradle中,否则有些方法无法监听到
this.gradle.addBuildListener(new BuildListener() {
    @Override
    void buildStarted(Gradle gradle) {
        println(prefix + "buildStarted ")
    }

    @Override
    void settingsEvaluated(Settings settings) {
        println(prefix + "settingsEvaluated ")
    }

    @Override
    void projectsLoaded(Gradle gradle) {
        println(prefix + "projectsLoaded 已经完成Gradle构建的初始化阶段")
        //会调用到对应的this.gradle.projectsLoaded{}
    }

    @Override
    void projectsEvaluated(Gradle gradle) {
        println(prefix + " projectsEvaluated 已经完成Gradle构建的配置阶段")
    }

    @Override
    void buildFinished(BuildResult buildResult) {
        println(prefix + "buildFinished  已经完成Gralde构建的执行阶段")
    }
})

另外如果只想监听某个生命周期方法,可以直接通过单独调用生命周期对应的方法即可,如下:

this.gradle.projectsLoaded {
    
}

对于其他过程的监听都可以采用 this.gradle.addBuildListener()的方式加入对应的监听器,或者直接调用对应的监听函数即可。其他的监听有:

  • org.gradle.api.execution.TaskExecutionGraphListener
  • org.gradle.api.ProjectEvaluationListener
  • org.gradle.api.execution.TaskExecutionListener
  • org.gradle.api.execution.TaskActionListener
  • .......

1.Gradle构建项目之初始化阶段

(1)作用

该阶段主要是创建项目层次结构,并且为每个Module创建一个Project实例。到底该项目中有多少个Project实例,相关脚本就是settings.gradle。settings.gradle决定将哪些项目进行参与构建。查看下settings.gradle下的内容如下:

rootProject.name = "AndroidPlugin"
include ':app'
include ':firstplugin'

通常在项目开发的时候,需要将编译打包的module添加到该文件中。一个settings.gradle脚本对应一个Settings对象,在初始化阶段就会构建一个Settings的实例对象,同时为该实例下的各个module分别创建出对应的Project实例。

(2)相关生命周期方法

初始化阶段结束时会回调到projectsLoaded(gradle),此时完成初始化阶段,才可以获取到所有的Project的信息,如下:

this.gradle.allprojects {
    Project project ->
        println(prefix + " allprojects Project name =  "+project.name)
}

通过Sync该settings.gradle文件,通过输出的内容看到,仅仅在projectsLoaded(gradle)回调结束之后,才会获取到Project实例信息,在之前的生命周期方法之前,是没法获取Project信息的。

<<<<<<settings.gradle<<<<<<<<<<< settingsEvaluated 
<<<<<<<settings.gradle<<<<<<<<<<< projectsLoaded 已经完成Gradle构建的初始化阶段
<<<<<<<settings.gradle<<<<<<<<<<<  allprojects Project name =  AndroidPlugin
<<<<<<<settings.gradle<<<<<<<<<<<  allprojects Project name =  app
<<<<<<<settings.gradle<<<<<<<<<<<  allprojects Project name =  firstplugin

另外settingsEvaluated(Settings settings) 中返回的settings就是该settings.gradle文件,在此方法中可以获取到setting.gradle文件到实例对象Settings。

2.Gradle构建项目之配置阶段

(1)作用

该阶段主要完成每个Project的配置以及各个Task任务依赖关系图,以便在执行阶段按照此依赖关系执行task。

在初始化阶段已经将每个Module初始化为一个Project对象,那么在该阶段就是执行每个build.gradle脚本中的各种语句、闭包以及Task的配置段语句。

  • 1)工程根目录

在工程的根目录下会有一个build.gradle文件,代码如下:

buildscript {
    //1.所依赖的插件库的下载仓库
    repositories {
        google()
        mavenCentral()
        maven{
            //maven本地仓库
            url uri('plugins')
        }
    }
    //2.标识各个gradle插件
    dependencies {
        classpath "com.android.tools.build:gradle:4.2.1"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
        classpath "com.wj.plugin:FirstPlugin:1.0.0"
    }
}

allprojects {
    //3.该Project下所有模块的公共配置
    repositories {
        google()
        mavenCentral()
        jcenter() // Warning: this repository is going to shut down soon
    }
}
    //4.特设任务
task clean(type: Delete) {
    delete rootProject.buildDir
}

主要就是作用就是:

1)一些项目依赖所需要的Gradle插件的下载仓库地址

2)Gradle插件的唯一标识

3)该Project下的所有Module设置一些公共的配置

4)某些特定的Task

遗留问题:这些特定的Task默认是Android Gradle执行什么操作的时候执行呢?因为在执行这个根目录的clean任务的时候,发现实际并没有执行。

  • 2)每个Module的根目录

在每个Module下也会有一个build.gradle文件,大体内容如下:

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.3"

    defaultConfig {
       ......
    }
    ......
}


firstPluginConfig{
    sdkVersion '1.0.0'
}

dependencies {
 ......
}

里面的内容主要是针对当前module下的一些Project的配置信息:

1)像android{}就是一些闭包,就来指定项目编译版本等信息;

2)firstPluginConfig就是在自定义Gradle插件中的配置信息;

3)dependencies {}闭包就是定义了该Module的一些依赖,通常是一些本地依赖、库依赖和远程依赖。

这个project接口是build.gradle与Gradle交互的主要API,可以访问所有的Gradle功能。因为一个build.gradle就是一个Project实例,那么在build.gradle可以直接调用Project中的相关方法,例如

println(this.name)

此语句就是输出该Project实例的名字,和在一个实现了 Plugin<Project>接口的类里面,直接通过下面获取的方式是一样的。

   System.out.println("================"+project.name)

2)相关生命周期方法

在该阶段会循环执行每个module下的build.gradle文件,会整个配置阶段结束之后,会回调到projectsEvaluated(gradle)方法。在整个过程中可以通过以下方法来监听每个build.gradle在执行之前和执行之后的hook点,代码如下:

//循环每个build.gradle来构建task依赖关系图
this.gradle.beforeProject {
    Project project ->
        println(prefix + "beforeProject Project name = " + project.name)
}

this.gradle.afterProject {
    Project project ->
        println(prefix + "afterProject Project name  = " + project.name)
}

通过Sync该settings.gradle文件,通过输出的内容可以看到,当循环完成所有的Project实例的配置之后,就会回调到projectsEvaluated(gradle)方法,完成整个项目工程的配置阶段。


> Configure project :
<<<<<<<settings.gradle<<<<<<<<<<< beforeProject Project name = AndroidPlugin
<<<<<<<settings.gradle<<<<<<<<<<< afterProject Project name  = AndroidPlugin

> Configure project :app
<<<<<<<settings.gradle<<<<<<<<<<< beforeProject Project name = app
<<<<<<<settings.gradle<<<<<<<<<<< afterProject Project name  = app

> Configure project :firstplugin
<<<<<<<settings.gradle<<<<<<<<<<< beforeProject Project name = firstplugin
<<<<<<<settings.gradle<<<<<<<<<<< afterProject Project name  = firstplugin
<<<<<<<settings.gradle<<<<<<<<<<<  projectsEvaluated 已经完成Gradle构建的配置阶段

 此时就可以输出该整个项目工程中的所有task,代码如下:

//输出task关系图中的所有task
this.gradle.taskGraph.whenReady {
    List<Task> taskList = this.gradle.taskGraph.allTasks
    for (Task task : taskList) {
        println(prefix + " in graph of task  = " + task.name+" in project = "+task.getProject())
    }
}

该task仅在Android Studio执行build的时候,才会输出,如果仅仅是Sync settings.gradle文件,是看不到输出该内容的。

遗留问题:

1.大胆猜想下,当在build.gradle中定义的Task的时候,应该有一个运行的任务的依赖关系,在自定义Task的时候,需要添加这个执行顺序。这个可能就是自己在项目中自定义的Task没有执行的原因

2.但是反过来看这个问题:为什么Android Studio自动添加在项目工程根目录下build.gradle下的clean可以执行,而按照相同的方式复制的Task却不能执行呢?并且也不在该taskGraph中

因为输出的内容比较多,不在这边罗列。

当然也可以在每个build.gradle去单独监听每个Project的配置过程,即在每个module的build.gradle下添加如下代码:

//配置前
this.beforeEvaluate {
    println("in build.gradle beforeEvaluate")

}
//配置后
this.afterEvaluate {
    println("in build.gradle afterEvaluate ")

}

3.Gradle构建项目之执行阶段

(1)作用

该阶段主要就是执行task任务,完成最终目标的生成。一个Project可以有多个task,task之间可以相互依赖。task是Gradle中执行的最小单元。task都会封装到Gradle插件中,当然也可以在build.gradle中直接定义task,例如通常在新建一个Android项目的时候,都会在项目的根目录的build.gradle中的有一个clean的task,如下

task clean(type: Delete) {
    delete rootProject.buildDir
}

具体Task的一些内容后面在详细总结下。这里也有一些自己暂时还没有找到答案的地方。

遗留问题:

1.为什么AndroidStudio自动添加到根目录的build.gradle文件中的clean就可以在taskGraph中,而按照同样方式添加的自定义task却不能执行

2.难道在添加自定义Task的时候,必须指定Task的执行顺序?

(2)相关生命周期方法

该阶段为整个Gradle构建项目过程中的最后一个阶段,完成最后的构建过程,最后回调到buildFinished(BuildResult buildResult)方法。因为该阶段是执行每个Task,同样Gradle也提供了相应的监听方法,提供了Hook点,代码如下:

this.gradle.taskGraph.beforeTask {
    Task task ->
        println(prefix + "beforeTask Task name = " + task.name)
}

this.gradle.taskGraph.afterTask {
    Task task ->
        println(prefix + "afterTask Task name = " + task.name)
}

在执行每个Task的时候,都会回调到这两个方法中,简单的列举一个,在Android Studio build项目的时候,输出的内容如下:

> Task :app:preDebugBuild UP-TO-DATE
<<<<<<<settings.gradle<<<<<<<<<<< beforeTask Task name = preDebugBuild
<<<<<<<settings.gradle<<<<<<<<<<< afterTask Task name = preDebugBuild

当然也可以在每个Task中,通过添加Action的方式来监听Task的执行过程,代码如下:

task clean(type: Delete) {
    delete rootProject.buildDir
    doLast {
        println(prefix + " clean task do last")
    }
    doFirst {
        println(prefix + " clean task do first")
    }
}

直到所有的Task执行完毕,最后回调到 buildFinished(BuildResult buildResult)方法。完成整个Gradle构建项目过程。

相关内容可直接查看github上的相关代码,该生命周期监听的内容都在根目录的settings.gradle中,github的地址:https://github.com/wenjing-bonnie/AndroidPlugin.git

二 Gradle中的Project

前面提到了一个settings.gradle就是一个Settings对象,通过Settings对象决定来将哪些Module加入到项目构建过程中; 每个Module中都含有一个build.gradle,一个build.gradle就是一个Project,并且通过Settings对象来创建出Project对象。

1.Project中的Task

一个Project本质上是一个Task的集合。在Project中的TaskContainer为一个Task集合。每一个Project在构建过程中都是有一系列的Task组成。每个Task主要就是完成一些具体的任务,例如编译、打包、发布等操作。

(1)定义Task

可直接在build.gradle文件中添加task,如下:

//自定义Task
task firstTask {
    println(prefix + "firstTask ")
}

task("secondTask") {
    println(prefix + "secondTask by (string) ")
}

task(thirdTask) {
    println(prefix + "thirdTask by () ")
}

其中firstTask、secondTask以及thirdTask均为该Task的名字。  

效果同在Project中通过下面的方式创建一个Task,代码如下:

    project.task("secondTaskInProject"){
            println("===== secondTaskInProject ==== ")
        }

该 secondTaskInProject就是创建的这个Task的名字。

(2)创建Task的其他方式

当然还提供了其他方式来创建自定义Task。

  • (1)继承已有的Task类型,如
task copyTask(type: Copy) {
    from 'copyTask.txt'
    into 'src/main'
    doLast {
        println(prefix + " Task of copy do last")
    }
    rename {
            //重命名
        String fileName ->
            fileName = "copyTaskOk.txt"
    }
}

其中Copy就是一个已有的可以完成复制功能的Task。当然里面还可以在这过程中增加一些filter、exclude、rename等操作。

其他已有的Task类型有:

  • 1)Sync:与Copy类似,区别在于当执行该Task的时候,会将源文件复制到目标目录,目标目录下所有的非复制文件将会被删除,除非指定Sync.preserve{}来增加匹配规则,保证符合匹配规则的文件不被删除。代码大体如下:
task syncTask(type: Sync) {
    from 'copyTask.txt'
    into 'src/main/synctest'
    preserve {
        //保持该文件不被覆盖
        exclude 'src/main/synctest/syncexist.txt'
    }
    doLast {
        println(prefix + "syncTask do last")
    }
}

若想src/main/synctest下的内容不被覆盖,就使用exclude 过滤掉不能被覆盖的内容,保持不被覆盖的内容。 

  • 2)Zip:创建Zip归档文件,默认的创建的文件为Zip
  • 3)Delete:删除
  • .......

详细的每个Task类型可参加官方API文档:https://docs.gradle.org/current/dsl/org.gradle.api.Task.html。从左侧可以链接进去,如图:

  • (2)直接继承DefaultTask
public class FirstPluginConfigTask extends DefaultTask {
}

然后通过下面的方式添加到Project中。 

  • 1)在Project中通过获取到当前Project中的TaskContainer
     //获取当前project中的TaskContainer
    TaskContainer taskContainer = project.getTasks()
  • 2)将Task加入到Project中
    //create():Creates a Task with the given name and adds it to this container.        
        taskContainer.create("secondTaskInProject", {
            println("===== secondTaskInProject ==== ")
        })

 然后通过TaskContainer.create()将该Task加入到Project中

    FirstPluginConfigTask task = taskContainer.create("firstPluginConfig", FirstPluginConfigTask.class)

其中FirstPluginConfigTask是一个继承了DefaultTask的一个Task,其中"firstPluginConfig"为该Task的名字,可以在build.gradle中直接使用。

(3)Action

其实Task的本质是一组被顺序的Action对象构成。可以把Action理解为一段代码块。可通过在Task中添加doFirst{}和doLast{}来为Task执行Action的开始和结束添加Action。像在3.Gradle构建项目之执行阶段为clean这个Task添加doFirst{}和doLast{},在执行clean的前后完成一些事件处理。

task clean(type: Delete) {
    delete rootProject.buildDir
    doLast {
        println(prefix + "Android Studio auto add clean task do last")
    }
    doFirst {
        println(prefix + "Android Studio auto add clean task do first")
    }
}

通常可以用闭包Closures的方式来实现添加一个Action。 

(4)Task的执行顺序

Gradle的官方文档介绍Task的API有这么一段话吸引了我,如下

A task may have dependencies on other tasks or might be scheduled to always run after another task. Gradle ensures that all task dependencies and ordering rules are honored when executing tasks, so that the task is executed after all of its dependencies and any "must run after" tasks have been executed.

一个Task可能依赖于其他tasks或者会要求运行在其他tasks之后。Gradle确保在执行这些Tasks时遵循这些tasks的依赖关系和顺序规则,这样这些Tasks会在依赖任务执行完之后或者“必须运行完”之后的Tasks后执行。

所以这是不是我在前面遗留问题的解答呢?就是每个Task在执行的时候,是需要设定执行的顺序的。

Task提供了下面几种方法来设定依赖关系或着执行顺序:

  • 1)BTask.dependsOn ATask:先执行完ATask,在执行BTask;
  • 2)BTask.mushRunAfter ATask:先执行完ATask,在执行BTask
  • 3)BTask.mushRunAfter ATask CTask.mushRunAfter ATask:按照ATask、BTask、CTask顺序执行
  • 4)BTask.shouldRunAfter ATask:先执行完ATask,在执行BTask

2.Project中的Dependencies

一个Project中还会有一些依赖库,才能使得Project运行成功。要想让一个依赖库能够成功添加到项目中,一般需要下面四个要素配合使用才可以起效果。

(1)ConfigurationContainer

用来管理对依赖库分组,通过该配置项来将依赖库进行分组。可以在不同的编译环境下运行不同的依赖库,每一组依赖库为一个Configuration。

在build.gradle通过可添加一个自定义的Configuration:

configurations {

}

效果同在Project中通过下面的代码获得当前Project的ConfigurationContainer:

    ConfigurationContainer configurationContainer = project.getConfigurations()

在Android Gradle插件中有一些标准配置,如implementation、 testImplementation、 androidTestImplementation 、provider,还有之前的compile、testCompile、androidTestCompile等等。

这个体现在我们通常在Android项目添加依赖的时候,通常如下:

dependencies {

    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.2.1'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

这个就是在不同的运行环境下,会将不同的依赖添加到项目中。

遗留问题:最终体现对应着Android studio怎么运行这个项目呢???

(2)DependencyHandler

定义构建项目时所依赖的类库。

在build.gradle文件中通过dependencies{}来定义构建项目时所依赖的类库。

  • 1)可以添加远程依赖,需要repositories {}配合添加相应的仓库
dependencies { 
    //gradle sdk 
    implementation gradleApi()
     //groovy sdk 
    implementation localGroovy() 
}
repositories {
    mavenCentral()
}
  • 2)可以添加本地依赖库,可以是项目project,可以是jar包、可以原生依赖库等

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    implementation project(':libs:WidgetLibrary')
    implementation files('libs/xxxxx.jar')
}

效果同在Project中可以通过DependencyHandler来进行管理这些依赖库。

    DependencyHandler dependencyHandler = project.getDependencies()

(3) RepositoryHandler

 定义添加的依赖库的仓库。即可以是本地仓库,也可以是远程仓库

在build.gradle中通过repositories {}添加的是依赖库的仓库的方式如下:

repositories {
     //maven远程仓库
    mavenCentral()
     //maven本地仓库
    maven{
        url uri('plugins')
     }
}

  效果同在Project中可以通过RepositoryHandler来管理依赖的仓库

     RepositoryHandler repositoryHandler = project.getRepositories()

(4)ArtifactHandler

定义Gradle插件发布的一种方式,如是jar还是pom等方式。

之前在在Android Gradle插件开发初次交手(一)中的4.插件发布使用的是通过maven来进行发布该插件,即将该插件发布到maven仓库。

而现在是通过在build.gradle中通过artifacts{}来配置Gradle插件被打包成jar包的方式,代码如下:

//==== 方法一:打包成Jar包的方式 begin
def marvenjar = 'mavenjar'
//默认名字是 [archiveBaseName]-[archiveAppendix]-[archiveVersion]-[archiveClassifier].[archiveExtension]
task makeJar(type: org.gradle.jvm.tasks.Jar) {
    //删除之前创建的文件夹
    delete marvenjar
    archiveBaseName = "firstplugin"
    archiveAppendix = "appendix"
    //为什么不起作用呢??
    archiveVersion = '1.0.0'
    archiveClassifier = 'classifiter'
    //扩展名,默认的为jar
    //archiveExtension = "extension"
    doLast {
        println "=== task jar is finished ==="
    }
}

artifacts {
    //将firstPluginJar看做artifact,交给archives管理
    archives makeJar
}

uploadArchives {
    repositories {
        maven {
            //在build.gradle的同级目录下生成mavenjar文件夹
            url "file:" + marvenjar
        }
    }
}

最终通过通过Android studio的gradle插件,uploadArchives一下,就会在build.gradle的同级目录下生成mavenjar文件夹下生成对应 [archiveBaseName]-[archiveAppendix]-[archiveVersion]-[archiveClassifier].[archiveExtension]命名方式的jar包。

遗留问题:archiveVersion不知道为什么不起作用,生成的名字该处为unspecified.

相关代码已经上传至https://github.com/wenjing-bonnie/AndroidPlugin.git。具体对应AndroidPlugin/firstplugin/build.gradle的相关代码。

效果同在Project中可以通过ArtifactHandler来进行配置Gradle插件发布方式。

    ArtifactHandler artifactHandler = project.getArtifacts()

三 Android Gradle中的buildSrc

Android Gradle插件开发初次交手(一)中我们通过新建一个Module的方式自定义一个"com.wj.firstplugin"的 Gradle插件,但是这种方式每次在修改这个Gradle插件的内容的时候,都需要通过Gradle工具的uploadArchives进行进行发布,那么Android Gradle在构建项目过程中提供了一种更方便的方式可以自定义一些具有特殊功能的Gradle插件:

在Android Gradle在构建项目的时候,会首先检测是否在根目录下存在buildSrc目录,而Gradle会自动编译并检测里面的代码,把相关的build.gradle加入到项目的构建脚本中。

所以针对这种特殊设置,那我们就可以在项目工程的根目录创建buildSrc目录,并且只能在项目工程的根目录下创建该文件夹,然后创建该groovy文件夹、resources文件夹、build.gradle文件(忽略里面的其他文件夹以及文件,这些都是在构建过程中产生的文件)。

另外还buildSrc已经被默认为一个固定的Module,不需要额外添加到settings.gradle文件中。

在该groovy目录下和Android Gradle插件开发初次交手(一)中的三、自定义Gradle插件方式添加相应的自定义Gradle插件的相关代码,就可以不需要发布,只需要通过Android Studio的build就可以实现该Gradle插件的功能。

具体的代码已经上传到至https://github.com/wenjing-bonnie/AndroidPlugin.git的buildSrc目录的相关内容。

四 猜测自定义Task在build.gradle中的执行问题

大胆猜测下前面在3.Gradle构建项目之执行阶段中提到的为什么在build.gradle中自定义的Task不执行的原因:暂定认为需要设置Task的依赖关系

因为在buildSrc中添加自定义的copyTask和syncTask的时候,为copyTask和syncTask添加了

assemble.dependsOn copyTask
assemble.dependsOn syncTask

然后发现在Android Studio build过程中,已经将 copyTask和syncTask添加到任务队列中,并且对应里面的任务内容都已经成功执行。

> Configure project :buildSrc
> Task :buildSrc:copyTask
~~~~~~GlobalGradleProject~~~~~~~~ Task of copy do last

> Task :buildSrc:compileJava NO-SOURCE
> Task :buildSrc:compileGroovy UP-TO-DATE
> Task :buildSrc:processResources UP-TO-DATE
> Task :buildSrc:classes UP-TO-DATE
> Task :buildSrc:jar UP-TO-DATE
> Task :buildSrc:generateSourceRoots UP-TO-DATE

> Task :buildSrc:syncTask
~~~~~~GlobalGradleProject~~~~~~~~syncTask do last

> Task :buildSrc:assemble
> Task :buildSrc:compileTestJava NO-SOURCE
> Task :buildSrc:compileTestGroovy NO-SOURCE
> Task :buildSrc:processTestResources NO-SOURCE
> Task :buildSrc:testClasses UP-TO-DATE
> Task :buildSrc:test SKIPPED
> Task :buildSrc:check SKIPPED
> Task :buildSrc:build

 遗留问题:

1.为什么“copyTask.dependsOn assemble”这张方式不行呢?先执行assemble再去执行copyTask,不可以吗?

2.并且为什么两个执行的顺序不是挨着呢?为什么syncTask在好几个任务后面呢?

3.那如果不通过这样依赖已有的Task任务,那有什么方式可以执行自定义的Task呢?

可通过dependsOn(ATask,BTask,CTask.....)来让ATask,BTask,CTask按照顺序执行。

4.为什么AndroidStudio自动在根目录的build.gradle生成的clean这个Task就可以自动执行,相反的在根目录下在添加其他Task就是不能执行呢?并且把clean这个Task换一个名字也不能执行?

 当然通过下面的方式可以让两个Task按照顺序执行,但是还是需要依赖已有的Task,代码如下:

this.afterEvaluate {
    tasks.matching {
        Task task->
            task.name.equals("compileJava")
    }.each {
        Task task ->
            println(prefix + " afterEvaluate task name = " + task.name)
            task.dependsOn(copyTask, syncTask)
    }
}

通过Android Studio build过程中,发现此时的 copyTask和syncTask也添加到任务队列中,并且相应的任务也在执行,如下:

> Configure project :buildSrc
~~~~~~GlobalGradleProject~~~~~~~~ afterEvaluate task name = compileJava

> Task :buildSrc:copyTask UP-TO-DATE
> Task :buildSrc:syncTask UP-TO-DATE
> Task :buildSrc:compileJava NO-SOURCE
> Task :buildSrc:compileGroovy UP-TO-DATE
> Task :buildSrc:processResources UP-TO-DATE
> Task :buildSrc:classes UP-TO-DATE
> Task :buildSrc:jar UP-TO-DATE
> Task :buildSrc:assemble UP-TO-DATE
> Task :buildSrc:compileTestJava NO-SOURCE
........

 这个就是在buildSrc这个Project配置完成之后,将copyTask和syncTask添加到任务队列中。

五 总结

  • 1.Gradle构建项目主要有三个过程:初始化阶段、配置阶段以及执行阶段;
  • 2.Gradle提供了一些生命周期的方法来监听整个构建过程,可直接通过gradle.直接调用到对应的生命周期方法;
  • 3.Gradle的生命周期方法的监听需要在对应监听方法的调用之前调用,否则会调用不到对应的方法;所以如果要对整个生命周期方法的监听,必须在settings.gradle文件中设置监听;
  • 4.在初始化阶段主要完成整个项目工程的层次结构,为每个Module生成对应的Project实例。完成之后会回调到this.gradel.projectsLoaded(),此时可以通过this.gradle.allprojects{}获取该项目工程下的为每个Module生成的Project对象;
  • 5.在配置阶段主要根据每个module下的build.gradle完成该module下的配置以及Task的依赖关系图
    • (1)每个build.gradle配置过程都会回调到this.gradle.beforeProject{} 和this.gradle.afterProject{}
    • (2)当然也可以在build.gradle文件中自行监听该过程,回调到this.beforeEvaluate {}和this.afterEvaluate {}
    • (3)所有的module中的build.gradle完成配置之后,会回调到this.gradle.projectsEvaluated{}
    • (4)在该阶段完成后,可以通过this.gradle.taskGraph.whenReady {}获取到所有的Task
  • 6.在执行阶段就是执行所有的Task,每个Task的执行的时候都会执行到this.gradle.taskGraph.beforeTask {}和this.gradle.taskGraph.afterTask {}。当然也可以在Task中自行通过doFirst{}和doLast{}来监听
  • 7.一个settings.gradle文件就对应一个Settings对象,每个build.gradle文件就对应一个Project对象,而每个Project是有一系列的Task组成的,而一个Task是有Action组成;
  • 8.Android Studio在构建项目的时候,会默认的去首先检测项目工程的根目录下是否含有buildSrc目录,也是在构建过程中处理的第一个build.gradle。可在该目录下自定义具有特定功能的Gradle插件。

感觉梳理了这几天,似乎对Gradle构建项目有了一点眉目,但是还需要继续研究下,还有好多内容需要去了解,譬如在整个过程中遗留问题中的自定义Task到底是怎么执行的,譬如在Android Studio的Gradle工具中显示的那些Task到底是干什么的。。。。。

加油!!!

 

以上是关于Android Gradle的基本概念梳理的主要内容,如果未能解决你的问题,请参考以下文章

Android Gradle 中的Transform

Android Gradle之Java字节码

Android 自定义Gradle插件的完整流程

Android 自定义Gradle插件的多层属性扩展

Android 自定义Gradle插件的多层属性扩展

Android 自定义Gradle插件的完整流程