Gradle系列之Gradle基础

Gradle系列之Groovy基础
Gradle系列之Gradle基础
Gradle系列之自定义插件
Android构建流程(Android Plugin)源码解析系列一:主流程

gradle的工作流程

gradle工作流程分为三步

  • Initialization : 这个阶段,主要是会分析哪些module将被构建。这个时候又一个很明显的操作就是这个阶段会解析settings.gradle,并创建project对象。
  • Configuration :这个阶段,主要是执行当前项目所有需要构建的module以及root project的build.gradle脚本;这个过程会形成每个项目的task依赖图。
  • Execution :这个阶段,执行我们指定的task任务,以及其依赖的任务

下面来看看gradle build过程中,我们可以监听到的回调函数

gradle.addBuildListener(new BuildListener() {
    @Override
    void buildStarted(Gradle gradle) { // 构建开始执行之前的回调

    }

    @Override
    void settingsEvaluated(Settings settings) { // setting 评估完成的回调,也就是settings.gradle加载执行完成的回调

    }

    @Override
    void projectsLoaded(Gradle gradle) { // project加载完成的回调

    }

    @Override
    void projectsEvaluated(Gradle gradle) { // project 评估完成的回调, 也就是所有的build.gradle脚本都加载执行完成的回调

    }

    @Override
    void buildFinished(BuildResult result) { // 构建完成的回调

    }

具体构建的代码如下:

private BuildResult doBuild(final Stage upTo) {
    return buildOperationExecutor.run("Run build", new Factory() {
        @Override
        public BuildResult create() {
            Throwable failure = null;
            try {
                //回调buildStarted,但此时init.gradle没有被执行,时机太早
                buildListener.buildStarted(gradle);
                doBuildStages(upTo);
            } catch (Throwable t) {
                failure = exceptionAnalyser.transform(t);
            }
            BuildResult buildResult = new BuildResult(upTo.name(), gradle, failure);
            buildListener.buildFinished(buildResult);  // task执行结束的回调函数
            if (failure != null) {
                throw new ReportedException(failure);
            }
            return buildResult;
        }
    });
}
private void doBuildStages(Stage upTo) {
// Evaluate init scripts 执行init脚本
    initScriptHandler.executeScripts(gradle);
// Calculate projects, 执行setting.gradle
    settingsLoader.findAndLoadSettings(gradle);
// Configure build 
    buildOperationExecutor.run("Configure build", new Runnable() {
        @Override
        public void run() {
            buildConfigurer.configure(gradle);
            if (!gradle.getStartParameter().isConfigureOnDemand()) {
                buildListener.projectsEvaluated(gradle);
            }
            modelConfigurationListener.onConfigure(gradle);
        }
    });
    if (upTo == Stage.Configure) {
        return;
    }
// Populate task graph
    buildOperationExecutor.run("Calculate task graph", new Runnable() {
        @Override
        public void run() {
            buildConfigurationActionExecuter.select(gradle);
            if (gradle.getStartParameter().isConfigureOnDemand()) {
                buildListener.projectsEvaluated(gradle);
            }
        }
    });
// Execute build
    buildOperationExecutor.run("Run tasks", new Runnable() {
        @Override
        public void run() {
            buildExecuter.execute(gradle);
        }
    });
    assert upTo == Stage.Build;
}

通过上面执行的代码可以看出,在执行构建的过程,会先执行buildStarted()回调函数,然后执行真正的构建步骤,最后执行buildFinished()回调函数。构建步骤:先加载并执行init脚本,然后加载settings.gradle脚本,这个时候根据其内容,确定要加载那些build.gradle脚本,并且为每一个创建一个project实例;然后加载每个build.gradle脚本,并生成task的依赖图;最会执行指定的task。
注意点:

  1. 我们在settings.gradle或者build.gradle中注册的buildListern回调函数都无法执行到buildStarted()回调,因为buildStarted()回调函数执行的时候,settings.gradle和build.gradle脚本都还没有执行。
  2. 在settings.gradle注册的buildListener回调函数,除了buildStarted()执行不到,其他都可以执行到
  3. 在build.gradle中注册的buildListener回调函数,只能执行到projectsEvaluated()和buildFinished()回调

同时我们也能在settings.gradle中更改执行project的路径和build脚本

project(':app').projectDir = new File(settingsDir, './app') // 配置指定项目的路径
project(':app').buildFileName = 'demo1.gradle' // 配置指定项目的build脚本的文件名

gradle的核心内容

project对象

上面已经讲过,每一个module就是一个project,根项目也就是rootProject;也可以理解成每一个build.gradle就是一个project对象。
我们能用到的project的两个函数

  • allprojects{} 获取的所有的project,包括rootProject
  • subprojects{} 获取的是除了rootProject的全部project
  • getPlugins{} 获取当前project的所有的plugin,也可以通过此对象为project添加plugin

因为我们通过project可以获取到其对应的配置信息,以及其上面的task的信息。同时我们也可以动态的添加task,以及应用插件(调用其apply()即可),以及修改一些配置。
下面看一个subprojects的例子:

project.ext.preDexLibs = !project.hasProperty('disablePreDex') // 如果编译时候的gradle命令 没有disablePreDex,那么是在本地,
// 我们可以开启编译的dexOptions(这样每个library都会打包成一个dex,最后在合并起来,会提升增量编译的速度;但是会将第一次编译的速度变慢,因为会打出多个dex包);
// 如果在服务端,加上此属性,禁止preDexLibrary;这样会加速打包的速度

// 服务端执行的打包命令: ./gradlew clean assemble -PdisablePreDex

// 不包括 root project
subprojects{
    //当前闭包的作用域,里面project对应其对应的project作用域
    project.plugins.whenPluginAdded { plugin ->
        if ('com.android.build.gradle.AppPlugin'.equals(plugin.class.name)){
            project.android.dexOptions.preDexLibraries = rootProject.ext.preDexLibs
        }else if ('com.android.build.gradle.LibraryPlugin'.equals(plugin.class.name)){
            project.android.dexOptions.preDexLibraries = rootProject.ext.preDexLibs
        }
    }
}

plugin的处理的例子:

task printPlugins << {
    // 获取所有的plugin
    project.getPlugins().all { plugin ->
        println plugin.getClass().toString()
    }

    println '---------------------'

    // 添加plugin
    project.getPlugins().apply(JavaBasePlugin.class)
}

task

task的创建

主要有两种方式:

  1. project.task(),也可以直接在build.gradle脚本中直接调用task()函数
  2. project.tasks.create(), tasks返回的是TaskContainer对象

下面看几个例子:

// 方法的抽取封装
task printFiles << {
    fileList("./test").each {file ->
        println "file list : ${file.absolutePath}"
    }
}

File[] fileList(String dir){
    file(dir).listFiles({file -> file.isFile()} as FileFilter).sort()
}
// 定义动态任务
4.times {
    task "task${it}" << {
        println "I'm dyamic task : task${it}"
    }
}
// 可以调用 gradle task1 调用上面定义的任务

task的behavior

  • doFirst{} task执行之前执行
  • doLast{} task执行之后执行
// add task behavior
task hello
hello.doFirst{
    println 'task之前执行的behavior'
}
hello.doLast {
    println 'task之后执行的behavior'
}
// << 默认是调用的doLast{}
hello << {
    println '<< 是 doLast的缩写形式'
}

behavior是可以多次添加,并且依次执行。

task的依赖关系

两种建立依赖的方式

// 增加依赖
task0.dependsOn task2,task3
task sayHi(dependsOn: hello){
    println 'hi, 你好'
}

task的依赖图构建

task compile << {
    println "compile source"
}

task compileTest(dependsOn: compile) << {
    println "test compile case"
    throw new RuntimeException("test exception!!")
}

task demoTest(dependsOn: [compile, compileTest]) << {
    println "test case"
}

task dist(dependsOn: [compile, demoTest]) << {
    println "building the distribution"
}

执行上面的代码之后,会创建一个task的依赖关系图,之后再执行某个task的时候,会先执行它依赖的task。
执行gradle dist; 执行task的顺序:compile -> compileTest -> demoTest -> dist

声明变量(属性)

  • 通过def直接声明局部变量,可以通过这个对象直接获取
  • 通过ext定义的额外属性,也可以直接通过此对象直接获取
    上面所指的对象:project、task、gradle

下面来看额外属性的定义方式:
比如定义project的额外属性:

  • project.ext 来添加
  • ext{} 闭包来添加
ext {
    extName = 'lufei'
}
project.ext.extDes = "ext description"
println project.extName
println project.extDes

task的配置

// 配置任务, 也就是使用闭包,声明任务的执行内容; 还有type,也就是支持对应的功能,比如Copy,如下
task initConfig(type: Copy) << {
    // include和exclude用于筛选文件
    from('src/main/config') {
        include '**/*.properties'
        include '**/*.xml'
    }
    from('src/main/config') {
        exclude '**/*.properties', '**/*.xml'
    }
    from('src/main/languages') {
        rename 'EN_US_(.*)', '$1'
    }
    into 'build/target/config'
    exclude '**/*.bak'

    includeEmptyDirs = false
}

task anotherCopyTask(type: Copy) {
        // Copy everything under src/main/webapp
        from 'src/main/webapp'
        // Copy a single file
        from 'src/staging/index.html'
        // Copy the output of a task
        from copyTask
        // Copy the output of a task using Task outputs explicitly.
        from copyTaskWithPatterns.outputs
        // Copy the contents of a Zip file
        from zipTree('src/main/assets.zip')
        // Determine the destination directory later

        // 增加筛选条件
        include '**/*.java'
        include '**/*.html'
        exclude { details ->
            details.file.name.endsWith('.html') && details.file.text.contains('staging')
        }
        into { getDestDir() }
}
// 创建归档文件 zip文件
    // 类似copy 任务,  要归档的文件可以进行筛选; 同时还可以对归档文件进行重命名; 具体详解 参考文档
    task myZip(type: Zip) {
        from 'somedir'
    }
// 配置为自定义的task
task hello1 (type: HelloTask) << {
}

task的排序

  • mustRunAfter
  • shouldRunAfter
testClosure.mustRunAfter configClosure

task的覆盖

// 重写 覆盖任务
task testClosure(overwrite: true) << {
    println "overwrite testClosure task"
}

task的跳过

  • 使用断言:nolyIf{} 只有满足条件才执行task
  • 抛出异常:抛出异常,并不会中断其他task的执行,只是不过跳过当前task
  • enable属性:默认是true,设置为false,当前task就不执行了
// 1. 使用断言, onlyIf  : 当满足条件才执行task
testClosure.onlyIf {
    project.hasProperty("skipTest")
}

// 抛出 StopExecutionException 异常,终止当前task的执行
task stopExceptionTask <<{
    println 'StopExecutionException 中断执行结果'
}
stopExceptionTask.doFirst {
    throw new StopExecutionException()
}

// task的 enabled 为 false, 也会跳过执行当前task
testClosure.enabled = false

gradle文件操作

  • project.file():直接获取文件,可以通过相对路径、绝对路径、file对象获取,同java
  • project.files():返回的是 FileCollection对象,是一个集合对象
  • project.fileTree():FileTree 文件树, 继承自 FileCollection, 用于表示一个目录树或ZIP文件内容。 source sets 继承FileCollection接口;
  • project.zipTree(): 用于处理zip文件
  • project.tarTree(): 用于处理tar文件
  • project.resources.zip():用于压缩文件

project.files()的例子如下:

task testFiles() << {
    FileCollection collection = files('a.txt', new File('b.txt'), ['CMakeLists.txt', 'demo.gradle'])
    collection.each { file ->
        println file.absolutePath
    }
    // FileCollection 转换成其他集合
    Set set = collection.files
    Set set2 = collection as Set;
    List list = collection as List
    String path = collection.asPath  // 将所有文件的绝对路径 用 : 连接
    println path
//    println collection.singleFile  // 报错, 因为有四个文件
//    println (collection as File)   // 报错, 同上

    // 通过 加号 和 减号  直接操作集合
    def union = collection + files("test/a.txt")
    def differernt = union - files("a.txt")

    differernt.each { file ->
        println file.path
    }

}

project.fileTree()的例子如下:

task fileTreeDemo << {
    FileTree tree = fileTree(dir: 'src/main')
    // 对目录中的文件进行筛选
    tree.include '**/*.java'
    tree.exclude '**/Abstract*'

    // fileTree()增加第二个参数, 使用闭包的方式添加筛选条件
    tree = fileTree(dir: 'src/main', {
        include '**/*.java'
    })

    tree = fileTree(dir: 'src', includes: '**/*.java')

    //遍历一个tree
    tree.each { File file ->
        println file.absolutePath
    }

    // filter 筛选
    FileTree filterTree = tree.matching {
        include '**/*.java'
    }

    // 也可以通过直接 加减 来处理FileTree
}

zipTree()和tarTree()获取到的FileTree之后,使用和原来一样, 可以通过此FileTree来增加归档文件的大小,例如:往里面添加文件。

文件的压缩:

resources.gzip('src/a.txt')

gradle build过程的回调函数

project的脚本执行完成的回调

        project.getGradle().addProjectEvaluationListener(new ProjectEvaluationListener() {
            @Override
            void beforeEvaluate(Project p) {

            }

            @Override
            void afterEvaluate(Project p, ProjectState projectState) {

            }
        })

构建task依赖图完成的回调函数

        project.getGradle().addListener(new TaskExecutionGraphListener(){

            // 构建task依赖图 完成的回调, 回调参数graph可以获取到需要构建的任务的所有依赖
            @Override
            void graphPopulated(TaskExecutionGraph taskExecutionGraph) {
                taskExecutionGraph.allTasks.each { task ->
                    // 获取到当前构建图的所有task
                    println "task name is ${task.getName()}"
                }
            }
        })

task 执行前后的回调函数

        project.getGradle().addListener(new TaskExecutionListener(){
            @Override
            void beforeExecute(Task task) {

            }

            @Override
            void afterExecute(Task task, TaskState taskState) {

            }
        })

task所有的action执行前后的回调函数

        project.getGradle().addListener(new TaskActionListener() {
            @Override
            void beforeActions(Task task) {

            }

            @Override
            void afterActions(Task task) {
                if (task instanceof HelloTask && task.getActions().size() > 0){
                    task.getActions().each {action ->
                        println "Hello Plugin action : "
                        action.execute(task)
                    }
                }
            }
        })

处理task的denpenency(依赖包)前后的回调函数

        project.getGradle().addListener(new DependencyResolutionListener(){
            @Override
            void beforeResolve(ResolvableDependencies resolvableDependencies) {

            }

            @Override
            void afterResolve(ResolvableDependencies resolvableDependencies) { // 处理依赖完成的回调函数
                // 下面例子: 检测release构建如果含有snapshot包或beta包,就会抛出异常
                def projectPath = resolvableDependencies.path.toLowerCase()
                // 对应的是releaseCompileClassPath task
                if(projectPath.contains("releasecompile")){
                    resolvableDependencies.resolutionResult.allDependencies.each { dependecy ->
                        if(dependecy instanceof DefaultUnresolvedDependencyResult){ // 没有处理的依赖
                        }else if (dependecy instanceof DefaultResolvedDependencyResult){
                            String selected = dependecy.selected
                            def from = dependecy.from
                            if(selected != null && (selected.toLowerCase().contains('snapshot') || selected.toLowerCase().contains('beta'))){
                                String errorMsg = "${selected} from ${from} contains a snapshot or beta version"
                                throw new IllegalStateException(errorMsg)
                            }
                        }
                    }
                }
            }
        })

gradle的taskGraph对象的回调函数

        // taskGraph构建完成的回调函数
        project.getGradle().taskGraph.whenReady { taskGraph ->
            taskGraph.afterTask { task ->
                println "=========whenReady : taskGraph : afterTask : ${task.getName()}=========="
            }
        }

        // task执行前后的回调函数,同上面gradle注册的TaskExecutionListener
        project.getGradle().taskGraph.addTaskExecutionListener(new TaskExecutionListener() {
            @Override
            void beforeExecute(Task task) {
                println "=========taskGraph : TaskExecutionListener : beforeExcute : ${task.getName()}==========="
            }

            @Override
            void afterExecute(Task task, TaskState taskState) {
                println "=========taskGraph : TaskExecutionListener : afterExcute : ${task.getName()}==========="
            }
        })
        // taskGraph构建完成的回调函数,同上面gradle注册的TaskExecutionGraphListener
        project.getGradle().taskGraph.addTaskExecutionGraphListener(new TaskExecutionGraphListener() {
            @Override
            void graphPopulated(TaskExecutionGraph taskExecutionGraph) {

            }
        })

你可能感兴趣的:(gradle)