Gradle基础到进阶——Gradle基础自定义插件(三)

环境配置

  • 官方地址:https://gradle.org/releases/
  • 下载之后将gradle下面的bin目录添加到系统的Path中(Android studio目录一般都是在.gradle目录下)


    image.png
  • 测试是否配置好:gradle -v

Hello Gradle

  • 新建一个build.gradle
println("Hello Gradle")
  • cmd定位到新建文件目录下
gradle help
  • 查看输出结果


    image.png
  • build.gradle是构建Project的核心文件,也是入口:
    • 如果没有该文件,会出现not found in root project 'xxxxx' 提示异常。
    • 必须要有一个可以运行的task,运行后自动生成.gradle文件夹下的内容

gradle wrapper

  • Gradle Wrapper用来配置开发过程中用到的Gradle构建工具版本。避免因为Gradle不统一带来的不必要的问题
  • 在工程目录下使用cmd命令生成wrapper:
gradle wrapper
  • 标准的gradle工程目录
    • gradlew和gradlew.bat分别是Linux和Windows下的可执行脚本
    • 具体业务逻辑是在/gradle/wrapper/gradle-wrapper.jar中实现
    • gradlew最终还是使用Java执行这个jar包来执行相关的Gradle操作


      image.png

gradle-wrapper.properties

image.png
  • distributionBase:下载的Gradle压缩包解压后存储的主目录
  • distributionPath:相对于distributionBase的解压后的Gradle压缩包的路径
  • distributionUrl:Gradle发行版压缩包的下载地址
    • bin:二进制发布版。
    • all:bin基础上还包含了源码和文档。
  • zipStoreBase:同distributionBase,只不过存放的是zip压缩包的
  • zipStorePath:同distributionPath,只不过存放的是zip压缩包的

gradle构建机制

android gradle工程目录

image.png

Gradle DSL

  • DSL(Domain Specific Language) 领域特定语言,或领域专属语言。简单来说就是专门关注某一领域的语言,它在于专而不是全,最典型的比如HTML。
  • Gradle可以使用Groovy DSL,专门用来开发Gradle的构建脚本。所以说Gradle整体设计是以作为一种语言为导向的,并非成为一个严格死板的框架

settings.gradle

image.png

  • Gradle支持多工程构建,使用settings.gradle来配置添加子工程(模块)。
  • settings文件在初始化阶段执行,创建Settings对象,在执行脚本时调用该对象的方法。
  • Settings.include(String... projectPaths):
    • 将给定的目录添加到项目构建中,':app'表示文件相对路径,相当于'./app'文件夹。
    • 多项目架构进行分层,把同层次的子工程放在同一文件夹下便于管理,使用':xxx:yyy'表示

build.gradle

  • build.gradle是项目构建文件,每个工程都有一个build.gradle文件。
  • build.gradle在配置阶段执行,并创建相应工程的Project对象,执行的代码可以直接调用该对象提供的方法或属性

Daemon(守护进程)

  • 项目启动时,会开启一个client,然后启动一个daemon,通过client向daemon收发请求,项目关闭,client关闭,daemon保持启动,有类似项目再次部署时,会直接通过新的client访问已经启动的daemon,所以速度很快,默认daemon不使用3小时后关闭;不同项目兼容性考虑,也可使用--no-daemon 启动项目,就没有速度优势了
  • 手动停止daemon:gradle wrapper --stop

生命周期

  • Initialization
    • Gradle支持单项目和多项目构建。在初始化阶段,Gradle确定哪些项目参与构建,并为每个项目创建project实例,比如解析setting.gradle
  • Configuration
    • 配置阶段,解析每个工程的build.gradle文件,创建要执行的任务子集和确定各种任务之间的关系,并对任务做一些初始化配置
    • 解析过程中并不会执行各个build.gradle中的task
    • 经过Configration阶段,Project之间及内部Task之间的关系就确定了
    • 一个Project包含很多Task,每个Task之间有依赖关系。Configuration会建立一个有向图来描述Task之间的依赖关系
    • 所有Project配置完成后,会有一个回调project.afterEvaluate(),表示所有的模块都已经配置完了
  • Execution
    • 运行阶段,Gradle根据配置阶段创建和配置要执行的任务子集,执行任务

执行流程

image.png

自定义任务

task

  • task是gradle中最小的任务单元,任务之间可以进行复杂的操作(如动态创建任务,多任务间依赖调用等等)。gradle的执行其实就是由各种任务组合执行,来对项目进行构建的
  • gradlew tasks -all命令查看所有任务
  • gradlew A B 命令表示执行任务A和B

自定义任务

  • build.gradle中自定义任务
    • task(任务名){}


      image.png
  • { }执行的是配置阶段的代码,执行阶段要处理的逻辑需要调用doFirst、doLast方法,在闭包中实现。
    • doFirst{}表示任务执行开始时调用的方法
    • doLast{}表示任务执行结束调用的方法。
  • task A(dependsOn:[B]){} 表示任务A依赖于任务B,那么B执行在A之前。
task A {
    println("Hello A")
    doLast {
        println("doLast A")
    }
}
task B {
    println("Hello B")
    doLast {
        println("doLast B")
    }
}

task test(dependsOn: [A, B]) {
    doLast {
        println("doLast test")
    }
}
A.dependsOn(B)
  • 自定义的任务默认分组到other中。

DefaultTask

  • task定义的任务其实就是DefaultTask的一种具体实现类的对象。
  • 可以使用自定义类继承DeaflutTask:
    • 在方法上使用@TaskAction注解,表示任务运行时调用的方法。
    • 使用@Input表示对任务的输入参数。
    • 使用@OutputFile表示任务输出文件。
    • 使用inputs,outputs直接设置任务输入/输出项。
    • 一个任务的输出项可以作为另一个任务的输入项 (隐式依赖关系)。

获取任务的类型

task A {

}
println(A.class)//class org.gradle.api.DefaultTask_Decorated

自定义任务类型实现自定义任务

  • 创建一个自定义任务


    image.png
  • 自定义任务实现文件的输入输出
class MyTask extends DefaultTask {

    String from
    File out

    @TaskAction
    void method() {
        println "我是自定义任务"
        //文件进行操作
        println inputs.files.singleFile
        def inFile = inputs.files.singleFile
         //将build.gradle文件复制到test文件
        def outFile = outputs.files.singleFile
        outFile.createNewFile()
        outFile.text = inFile.text
    }
}

task myTask(type: MyTask) {
    from = "I am Peakmain"
    out = file("test.txt")
    inputs.file file('build.gradle')
    outputs.file file('test.txt')
}

细心的人可能会发现,我们执行多次的时候并没有打印,这是因为我们的内容没有改变,所以会直接跳过@TaskAction方法,如果我们想每次都执行TaskAction怎么解决,我们可以在构造函数中添加一行代码

    MyTask() {
        outputs.upToDateWhen {
            false
        }
    }

钩子函数

初始化阶段
android中的settings.gradle中主要是gradle的初始化,具体大家看上面Gradle执行流程图

gradle.projectsLoaded {
    println "gradle.projectsLoaded"
}
gradle.settingsEvaluated {
    println "gradle.settingsEvaluated"
}

Terminal中执行命令:gradlew help,我们便可以看到我们的打印


image.png

配置阶段
gradle对项目所有library生效,project只对当前library生效

gradle.beforeProject {
    println "gradle.beforeProject"
}

gradle.afterProject {
    println "gradle.afterProject"
}
 //所有配置完成之后执行
gradle.taskGraph.whenReady {
    println "gradle.taskGraph.whenReady"
}
//只对当前有效
project.beforeEvaluate {
 //root无效
    println "beforeEvaluate"
}
project.afterEvaluate {
    println "afterEvaluate"
}

结果


image.png

gradle设置监听

//为gradle设置监听
gradle.addProjectEvaluationListener(new ProjectEvaluationListener() {
    @Override
    void beforeEvaluate(Project project) {
        println "beforeEvaluate"
    }

    @Override
    void afterEvaluate(Project project, ProjectState projectState) {
        println "afterEvaluate"
    }
})
//设置构建监听
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) {

    }
})

Project

  • build.gradle在配置阶段会生成project实例,在build.gradle中直接调用方法或属性,实则是调用当前工程的project对象的方法或属性
  • 使用Project提供的api,更方便在多项目构建设置
    • project(":app"){}指定的project(这里是app)配置
    • allprojects{}所有的project配置
    • subprojects{}所有的子project 配置
    • buildscript{}此项目配置构建脚本类路径
image.png

属性扩展

  • 使用ext对任意对象属性进行扩展
    • 对project进行使用ext进行属性扩展,对所有子project可见
    • 一般在root project进行ext属性扩展,为子工程提供复用属性,通过rootProject直接访问
    • 任意对象都可以使用ext来添加属性:使用闭包,在闭包定义扩展属性。直接使用=赋值,添加扩展属性
    • 由谁进行ext调用,就属于谁的扩展属性
    • 在build.gradle中,默认当前工程的project对象,所以在build.gradle直接使用"ext="或者"ext{}"其实就是给project定义扩展属性
  • 使用gradle.properties以键值对形式定义属性,所有project可直接使用
ext{//相当于project.ext
    author="peakmain"
}
task myTask{
}
myTask.ext.username="peakmain"
println(myTask.ext.username)
println(myTask.username)

Gradle插件

脚本插件

afterEvaluate {
    println tasks.getByName("packageDebug")
    task zip(type: Zip) {
        archiveName "outputs.zip"// 输出的文件名字
        destinationDir file("${buildDir}/custom")// 输出的文件存放的文件夹
        from tasks.getByName("packageDebug").outputs.files// 输入的文件
        tasks.getByName("packageDebug").outputs.files.each {
            println it
        }
    }
}

二进制插件

class MyTask implements Plugin{

    @Override
    void apply(Project project) {
            println("MyTask")
    }
}
apply plugin:MyTask

自定义Gradle插件,实现packageDebug的打包

1.新建buildSrc目录
2.新建一个build.gradle,同步构建一下
3.新建文件夹src/main/java
4.build.gradle引入插件

apply plugin:"java"

5.新建PeakmainPlugin.java类

public class PeakmainPlugin implements Plugin {
    @Override
    public void apply(Project project) {
        project.afterEvaluate(it -> {
            System.out.println(it.getTasks().getByName("packageDebug"));
            Map> typeMap = new HashMap<>();
            typeMap.put("type", Zip.class);
            Zip zipTask = (Zip) project.task(typeMap, "zipDebug");
            zipTask.setArchiveName("outputs.zip");
            zipTask.setDestinationDir(new File(project.getBuildDir().getAbsolutePath() + "/custom"));
            zipTask.from(it.getTasks().getByName("packageDebug").getOutputs().getFiles());
        });
    }
}

6.app目录下的build.gradle引入插件,同步一下

apply plugin:PeakmainPlugin

7.AndroidStudio右边的Gradle的Other多了一个zipDebug


image.png

8.结果


image.png

你可能感兴趣的:(Gradle基础到进阶——Gradle基础自定义插件(三))