Gradle系列之Groovy基础
Gradle系列之Gradle基础
Gradle系列之自定义插件
Android构建流程(Android Plugin)源码解析系列一:主流程
gradle工作流程分为三步
下面来看看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。
注意点:
同时我们也能在settings.gradle中更改执行project的路径和build脚本
project(':app').projectDir = new File(settingsDir, './app') // 配置指定项目的路径
project(':app').buildFileName = 'demo1.gradle' // 配置指定项目的build脚本的文件名
上面已经讲过,每一个module就是一个project,根项目也就是rootProject;也可以理解成每一个build.gradle就是一个project对象。
我们能用到的project的两个函数
因为我们通过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 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 调用上面定义的任务
// add task behavior
task hello
hello.doFirst{
println 'task之前执行的behavior'
}
hello.doLast {
println 'task之后执行的behavior'
}
// << 默认是调用的doLast{}
hello << {
println '<< 是 doLast的缩写形式'
}
behavior是可以多次添加,并且依次执行。
两种建立依赖的方式
// 增加依赖
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
下面来看额外属性的定义方式:
比如定义project的额外属性:
ext {
extName = 'lufei'
}
project.ext.extDes = "ext description"
println project.extName
println project.extDes
// 配置任务, 也就是使用闭包,声明任务的执行内容; 还有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) << {
}
testClosure.mustRunAfter configClosure
// 重写 覆盖任务
task testClosure(overwrite: true) << {
println "overwrite testClosure 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
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')
project.getGradle().addProjectEvaluationListener(new ProjectEvaluationListener() {
@Override
void beforeEvaluate(Project p) {
}
@Override
void afterEvaluate(Project p, ProjectState projectState) {
}
})
project.getGradle().addListener(new TaskExecutionGraphListener(){
// 构建task依赖图 完成的回调, 回调参数graph可以获取到需要构建的任务的所有依赖
@Override
void graphPopulated(TaskExecutionGraph taskExecutionGraph) {
taskExecutionGraph.allTasks.each { task ->
// 获取到当前构建图的所有task
println "task name is ${task.getName()}"
}
}
})
project.getGradle().addListener(new TaskExecutionListener(){
@Override
void beforeExecute(Task task) {
}
@Override
void afterExecute(Task task, TaskState taskState) {
}
})
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)
}
}
}
})
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)
}
}
}
}
}
})
// 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) {
}
})