Android 进阶——Android Studio 项目结构详细述及自定义扩展属性、Gradle 任务及构建生命周期(五)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/CrazyMo_/article/details/89053403

引言

前面的四篇文章中总结讲解了如何去利用Gradle 提供的属性和方法去灵活完成具体项目的构建,理论上来说对于大部分项目都已经足够了,但是并不意味着Gradle 仅仅支持这些功能,Gradle 作为高度灵活的构建系统,还支持扩展属性、自定义任务等一些高级功能。

一、Gradle 自定义扩展属性

所谓Gradle 自定义扩展属性本质就是自定义Groovy 中的变量,与其他语言一样,在Gradle脚本中我们可以通过很多种方式在build.gradle脚本(Project 或者Module目录下的)或者gradle.properties中定义属性(变量)

1、使用 def 变量名定义局部变量

使用关键字 def 可以只在声明变量的Gradle脚本文件中生效的局部变量,作用域仅在当前Gradle脚本。

//定义局部变量
def loaclParam="crazy.mo"

在这里插入图片描述

2、使用ext {}代码块声明全局变量

使用ext{}代码块声明的变量为全局变量,作用域对所有子项目可见。

//在Project 下的build.gradle脚本下通过ext代码块定义一个所有子项目可见的全局变量
ext {
    globalParam="对所有子项目可见的变量"
}

在这里插入图片描述

3、使用android.ext. 或者dependencies.ext .等关键字定义局部变量

在Project 类中任何对象都可以设置ext 也可以通过

//定义局部变量
def loaclParam="crazy.mo"
dependencies.ext.localParam2="局部变量2"
android.ext.localParam3="局部变量3"
ext.localParam4="局部变量4"
//showLocal.ext.localParam5="局部变量5" 不能再这里定义,因为代码是从上往下开始执行的,此时showLocal对象还未生成
task showLocal << {
    println("局部变量2:" + loaclParam)
    println("局部变量3:" + dependencies.ext.localParam2)
    println("局部变量:" + android.ext.localParam3)
    //println("局部变量:" + ext.localParam4)//这会报错不能这样访问,因为这样直接写相当于是去访问task下的ext.
    println("局部变量4:" + localParam4)
    println("局部变量4:" + project.ext.localParam4)//project 代表当前module下的,而rootProject则代表父Project下的
    println("局部变量5:" + ext.localParam5)

    println("ext:" + ext)
    println("project.ext:" + project.ext)
    println("showLocal.ext:" + showLocal.ext)
}
showLocal.ext.localParam5="局部变量5"

在这里插入图片描述

4、在gradle.properties中定义全局变量

在这里插入图片描述

二、Gradle 任务task

Gradle 完成构建操作本质上是通过一个个任务去完成的,如下图所示可以看到个任务的分组情况和依赖情况,所谓依赖就是执行某个任务的时候会自动去触发相依赖的任务,这也是为啥有时候我我们执行执行了assembleRelease任务,前面还会先去执行其他一系列的任务。
在这里插入图片描述

可以通过gradlew task --all 查看所有任务
也可以通过gradlew taskname 执行指定任务,甚至还可以通过驼峰缩写形式执行指定任务
(windows 环境下使用gradlew ,类Unix 使用./gradlew)

1、通过task 名称形式定义任务

通过关键字task 创建了一个任务时同时也创建了一个变量名称为task名的对象,如果不需要参数可以省略掉小括号,以下两种形式等价。
在这里插入图片描述

2、task 的生命周期

默认新建的task 对应的就是AbstractTask对象,而AbstractTask定义了几个周期方法,分别在task 任务配置期间doFirst执行任务前doLast 执行任务后去自动触发。

task customTask{
    println("在任务配置期间执行")
    doLast (new Action<Task>() {
        @Override
        void execute(Task task) {
            println("任务开始执行后3...")
        }
    })
    doLast (new Action<Task>() {
        @Override
        void execute(Task task) {
            println("任务开始执行后...")
        }
    })
    doFirst(new Action<Task>() {
        @Override
        void execute(Task task) {
            println("任务开始执行前...")
        }
    })
    doFirst(new Action<Task>() {
        @Override
        void execute(Task task) {
            println("任务开始执行前2...")
        }
    })
    doLast (new Action<Task>() {
        @Override
        void execute(Task task) {
            println("任务开始执行后2...")
        }
    })
}

customTask.doLast(new Action<Task>() {
    @Override
    void execute(Task task) {
        println("任务开始执行后 finaly...")
    }
})

在这里插入图片描述
另外在doLast 还有一种简洁的写法:
在这里插入图片描述

3、创建任务的执行顺序

1、通过dependsOn 指定执行顺序

通过dependsOn 指定taskThird在taskFirst和taskSecond 之后执行且taskSecond 在taskFirst之后执行。

task taskFirst <<{
    println('第一个任务被执行')
}

task taskSecond <<{
    println('第二个任务被执行')
}

//如果不指定taskFirst dependsOn taskSecond,即使把taskSecond写在前面taskSecond也不会先执行
task taskThird(dependsOn:[taskSecond,taskFirst]) <<{
    println('第三个任务被执行')
}

taskSecond dependsOn taskFirst 

2、通过mustRunAftershouldRunAfterfinalizedBy指定执行顺序

这些方法在AbstractTask中被实现。

//taskSecond dependsOn taskFirst
//必须在taskSecond之后执行
taskFirst.mustRunAfter taskSecond
//并行编译,可能在其后面也可能在之前
taskFirst.shouldRunAfter taskSecond

//假如说这个任务用于释放资源,清理缓存
task realseSrc <<{

}
//指定 realseSrc在taskThird 执行完毕之后运行
taskThird.finalizedBy realseSrc

4、继承AbstractTask 创建自定义的任务

4.1、继承AbstractTask 并使用@TaskAction 注解run方法

要定义自定义任务,必须需要使用@TaskAction 标注核心方法(比如下例中run方法为这个自定义任务的核心逻辑),然后可以根据需要自定义构造方法或增加属性。

//定义一个Task 任务
class CustomTask extends DefaultTask{
       CustomTask() {
        //[非必须]若不指定分组无论是通过集成还是task定义的任务都是默认存放于other分组中的,如果指定了group则会存放到定义的分组中
        group 'Custom分组'
        description '自定义任务的描述信息'
    }

    @TaskAction
    void run(){
        println("CustomTask 里真正处理的核心逻辑")
    }
}

4.2、把自定义任务添加到Gradle 构造系统

自定义任务添加到Gradle 构造系统后就可以通过gradlew 命令像执行系统的task 一样去执行自定义任务。

  • 通过 关键字tasks.create (String name, Class< T > type)
//添加到Gradle 构建系统 就可以通过gradlew mycustom 执行
tasks.create("mycustom",CustomTask)
  • 通过使用关键字task 创建任务时传入type值
//添加到Gradle 构建系统 就可以通过gradlew mycustom 执行就会自动去调用run方法
task mycustom(type:CustomTask){
}

4.3、使用@Input为自定义任务标注输入和@OutputFile 标注输出

每一个任务都是可以包含输出和输出的,通常第一次执行的时候会耗时较长,而之后则会比较快,这正是因为增量执行的缘故,而增量判定的依据就是两个用于标注的输入输出是否一致,如果一致则跳过任务的核心逻辑(跳过时就会显示UPDATE-TO-DATE)。

class CustomTask extends DefaultTask{
	//可以传入n个文件、属性、目录或者路径
    @Input
    @Optional
    String inputPath;
    @OutputFile
    File output;

    CustomTask() {
        //若不指定分组无论是通过集成还是task定义的任务都是默认存放于other分组中的,如果指定了group则会存放到定义的分组中
        group 'Custom分组'
        description '自定义任务的描述信息'
    }

    @TaskAction
    void run(){
        println("CustomTask 里真正处理的核心逻辑")
    }
}

如果指定了输入输出且没有使用@Optional标注(表示可选)的话则必须在任务配置阶段传入才能执行,否则会执行失败。

task mycustom(type:CustomTask){
    inputPath="xxx/xx/.xx"
    output="xx/xx.xxx"
}

4.4、设置Gradle 构建的方式

通过outputs.upToDateWhen{}改变Gradle的构建方式,默认为true,返回false时则每次都会全量执行,即关闭增量构建

class CustomTask extends DefaultTask{

    @Input
    String inputPath;
    @OutputFile
    File output;

    CustomTask() {
        //若不指定分组无论是通过集成还是task定义的任务都是默认存放于other分组中的,如果指定了group则会存放到定义的分组中
        group 'Custom分组'
        description '自定义任务的描述信息'
        outputs.upToDateWhen {
            //默认为true,返回false时则每次都会全量执行,即关闭增量构建
            true
        }
    }

    @TaskAction
    void run(){
        println("CustomTask 里真正处理的核心逻辑")
        //通过inputs.files拿到所有输入
        inputs.files.first()
        //inputs.files.singleFile()
    }
}

//mycustom 为CustomTask的增强任务,事实上无论是系统的自定义的使用这样的语法声明之后,就自动建立了映射 ,执行这个任务就相当于是执行了CustomTask中被 @TaskAction 标注的方法。
task mycustom(type:CustomTask){
    ///inputPath="xxx/xx/.xx" 传入输入
    //另一种方式通过调用file方法传入输入,就可以在run方法中通过inputs.files拿到输入信息
    inputs.file file("xxx/xx/.xx")
    output=file("xx/xx.xxx")
}

上例中mycustom 为CustomTask的增强任务,事实上无论是系统的自定义的使用这样的语法声明之后,就自动建立了映射 ,执行这个任务就相当于是执行了CustomTask中被 @TaskAction 标注的方法。

4.5、通过系统提供的api 创建增强型任务

Gradle自身就定义了很多任务,欲知更多请访问Gradle官网,接下来我们以使用系统知道的Zip压缩指定目录为例

//public class Zip extends AbstractArchiveTask
task myZip(type: Zip){
    //输出文件名
    archiveName "myzip.zip"
    //输出目录
    destinationDir file("${buildDir}/myzip")
    //设置指定目录为压缩源
    from "${buildDir}/ouputs/logs"
}

简单来说通过创建增强型任务,我们就可以在灵活地借助系统提供的api完成我们要的功能,这在后面进行自定义控件时非常好用。

4.6、通过tasks.getByName(“name”)获取系统任务

通过gradlew zip执行这个增强任务,系统的packageDebug相关的一系列任务会被先执行,然后把packageDebug任务的输出作为zip的输入被执行,最终把所有输出压缩为all.zip文件。

//在分析完 gradle完成之后才执行这个方法afterEvaluate
afterEvaluate {
    task zip(type: Zip) {
        archiveName 'my.zip'
        destinationDir file("${buildDir}/zip")
        println tasks.getByName('assembleDebug')
        from tasks.getByName('packageDebug').outputs.files
    }
}

在这里插入图片描述

三、Gradle构建生命周期

Gradle构建生命周期默认为以下三个步骤:

  • 首先是分析构建脚本,生成对应的Settings和Project类
  • 分别进行初始化配置
  • 执行对应的任务

而我们可以通过hook 来对Gradle 构建生命周期进行干预,只需要调用Gradle 提供的方法即可实现Hook。

//在分析完成gradle 之后执行
afterEvaluate {

}

//对于引入了android插件的工程无效
//beforeEvaluate {
//
//}

//gradle.addProjectEvaluationListener(new ProjectEvaluationListener() {
//    @Override
//    void beforeEvaluate(Project project) {
//
//    }
//
//    @Override
//    void afterEvaluate(Project project, ProjectState state) {
//
//    }
//})

//gradle 任务执行图
//gradle.addListener(new TaskExecutionGraphListener(){
//
//    @Override
//    void graphPopulated(TaskExecutionGraph graph) {
//        println graph.allTasks
//    }
//})

未完待续…

猜你喜欢

转载自blog.csdn.net/CrazyMo_/article/details/89053403
今日推荐