作为一个Android开发人员,相信大家每天开发都在与Gradle打交道,它是一款非常优秀的构建工具。基于Groovy语言实现,但是又与java完全兼容,在编写Gradle脚本的时候,完全可以按照java的语法去写相关的类,变量,方法。
Gradle 的生命周期分三个阶段,初始化阶段,配置阶段,执行阶段.那这三个阶段在做什么事情呢?请看每个阶段的描述
通过 settings.gradle 判断有哪些项目需要初始化,加载所有需要初始化的项目的build.gradle 文件并为每个项目创建 project 对象。setting里面的配置先初始化,然后是各个module中的build.gradle执行...
执行各项目下的 build.gradle 脚本,完成 project 的配置,并且构造 Task 任务依赖关系图以便在执行阶段按照依赖关系执行Task.执行task 中的配置代码,正如下面定义的task hello ,定义task时候配置的闭包(类似java的构造函数)。
task hello{
println('这是配置阶段执行的代码')
}
通过配置阶段的 Task 依赖关系图,按顺序执行需要执行的 任务中的动作代码,就是执行任务中写在action, doFirst 或 doLast 中的代码.在AndroidStudio中新建一个Android项目,在项目的根目录下会自动帮我们生成一个build.gradle文件,文件内容如下:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.1'
//相关插件的引用的地方
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
这个build.gralde是AndroidStudio自动我们生成的,这里面配置了repositories ,这个里面其实配置的是gradle的仓库,3.X.X之后,需要配置google(),AndroidStuido 会去相应的仓库去下载我们的插件。dependencies里面配置我们的插件,gradle插件是系统帮我们配置好的,只有配置好这个,在app module下的gradle才能使用application插件
apply plugin: 'com.android.application'
在这里我们也可以配置自己自定义的插件以及项目中引用的第三方库的插件。
这里面定义了一个task任务,名称为clean,type为Delete。当我们将光标定位到task clean这里的时候,task 是可以点进去的,类似于java当中的方法实现,点进去之后,我们可以看到,它其实是project中的一个方法(其实上面的build.gradle文件下的根节点 buildScript也是project当中的一个方法),这种定义的方法使得,我们可以在build中对其进行配置,然后gradle插件自动去解析生成对应的“闭包”,得到相关的属性,方法,任务。
那我们其实可以猜测一下,这个task clean任务 有一个参数,type其实就是我们构造task可以传递的一些参数:
可以看到默认的参数Task中默认的几个参数
配置项 | 描述 | 默认值 |
---|---|---|
name | 任务名称 | task 后面的紧跟的名称,不知道这个配置的作用 |
group | 用于配置任务的分组 | null |
type | 基于一个存在的task来创建,和累的继承相似 | DefaultTask |
dependsOn | 配置任务的依赖,描述一个任务基于另外一个任务执行 | [ ] |
action | 添加到任务中的一个action或者闭包 | null |
description | 配置任务的描述 | null |
overwrite | 是否替换存在的task,这个和type配合来使用 | false |
task clean(type: Delete) {
delete rootProject.buildDir
}
配置clean 任务的时候,或直接执行里面的代码,我们可以配置两个闭包,在执行task任务之前,doFrist执行一段代码,在执行task任务之后执行doLast里面的闭包。
我们在编译运行Android项目的时候,都会看到gradle为我们配置了许多运行Android项目需要执行的任务,这个在编译的时候,AndroidStudio底部的build栏可以看到:
例如我们在app module下的build.gradle中,定义一个任务hello:
task hello(group:'hellogroup',action:{
println('========= hello任务正在执行ing ===============') } ){
println('========= hello任务配置中 ===============')
doFirst {
println('========= hello任务执行之前 ===============')
}
doLast {
println('========= hello任务执行之后 ===============')
}
}
此时,我们会在右侧的gradle栏里面的other里面看到生成了一个gradle的任务,名称为hello,我们点击这个hello之后,在控制台就可以看到下面的输出:
可以看出action中配置的闭包,才是我们任务真正执行的时候干的事情,那么直接在task创建的时候。那么task任务执行的顺序我们基本可以确定如下:
1.构造Task任务时候的闭包,类似java类的构造函数
2.执行doFirst的闭包
3.执行action 配置的闭包
4.执行doLast的闭包
除了在build.gradle中定义task之外,我们也可以在java代码中定义task
在android项目中,新建一个module,注意这里选择的是java module,必须是java的module,并且 新建的module名称为buildSrc(注意名字必须是buildSrc,这是gralde的保留文件夹),新建moudule之后会是下面这个样子:
在buildSrc中定义java类,我们可以直接在module app下的build.gradle直接引用 使用,在module buildSrc中的build.gradle 配置相关依赖:
apply plugin: 'java-library'
repositories {
google()
jcenter()
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation gradleApi()
implementation 'com.android.tools.build:gradle:3.4.1'
}
sourceCompatibility = "7"
targetCompatibility = "7"
在这个文件夹中定义的类,我们可以直接在app module下的build.gradle的。我们可以自定义自己的Task,继承DefaultTask,如下面的实现方式:
package com.android.buildsrc;
import org.gradle.api.Action;
import org.gradle.api.DefaultTask;
import org.gradle.api.Task;
import org.gradle.api.tasks.TaskAction;
public class MyTask extends DefaultTask {
/**
* 注解 @TaskAction 标记的方法就是 task执行时候 执行的方法
*/
@TaskAction
public void executeMyTask(){
System.err.println("MyTask execute");
}
@Override
public Task doFirst(Action super Task> action) {
System.err.println("MyTask 的 doFirst执行");
return super.doFirst(action);
}
@Override
public Task doLast(Action super Task> action) {
System.err.println("MyTask 的 doLast 执行");
return super.doFirst(action);
}
}
那么可以在build.gradle中使用MyTask,下面的代码定义了一个名为xxxxx的task,Type为刚刚在java代码中定义的Task。
//类似java当中的使用 直接导入相关的java类
import com.android.buildsrc.MyTask
def MyTask mytask = task xxxxx(type:MyTask)
mytask.doFirst {
println('doFirst')
}
mytask.doLast {
println('doLast')
}
在我们的app module下面的build.gradle文件里面,我们还可以定义类,方法,变量等等。例如
def getBranch() {
def stdout = new ByteArrayOutputStream();
exec {
commandLine 'java','-version'
standardOutput = stdout
}
return stdout.toString()
}
getBranch()
通过Project下的exec方法,可以执行里面的闭包,我们可以执行命令行 的命令,这个例子是获取当前java的运行版本。我们还可以使用git命令获取当前的分支....使用加固命令来加固,等等都可以用这个闭包来进行操作,所有可以在定义插件或者Task,或者Transform中能够实现的功能,都能够直接用Groovy语言编写的脚本在build.gradle中实现。
而且在build.gradle中配置的参数,能够使用的参数,属性,都对应于Project中的一个闭包,或者AppExtention中的一个闭包.....当我们记不起可以配置什么属性的时候,可以点进去看看有哪些参数可以配置,以AppExtention android 为例(就是build.gradle中配置的android配置块)都是有层级的,看看下面的这段配置:
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
applicationId "com.android.androiddemo"
minSdkVersion 19
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
signingConfigs {
debug {
// No debug config
storeFile file("../store.jks")
storePassword "123456"
keyAlias "android"
keyPassword "123456"
}
release {
storeFile file("../store.jks")
storePassword "123456"
keyAlias "android"
keyPassword "123456"
}
}
buildTypes {
debug {
signingConfig signingConfigs.debug
minifyEnabled false
zipAlignEnabled false
shrinkResources false
}
release {
signingConfig signingConfigs.release
minifyEnabled true
zipAlignEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
}
我们的compileSdkVersion鼠标定位点击,可以点击进去,定位到BaseExtention的一个方法
可以看到都是对应一个方法,当我们不知道或者只能记住某个配置的大概名字的时候,我们就可以到这里搜索。很方便。
例如我们打包apk的时候,系统都会生成app-debug.apk 或者app-release.apk,如果我们想对文件改个名,可以像下面这样做:
applicationVariants.all {
variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null && !outputFile.name.contains("unaligned") && variant.buildType.name == "release") {
def fileName = "${buildInfo.app_name}_v${defaultConfig.versionName}_${buildInfo.build_time}_${variant.buildType.name}.apk"
//output.outputFile就是apk的输出文件
output.outputFile = new File(project.buildDir.absolutePath + "/outputs/apk/" + fileName)
}
}
}
其实applicationVariants也对应于AppExtention中的一个方法:
通过这个applicationVariants 我们可以获得许多配置信息,或者修改许多配置信息。我们应该学会去利用这些源代码,找到其中的闭包和方法,配置或者修改我们项目中的属性或者参数。