gradle学习笔记之hook task

这周在项目中加入hotfix功能,在gradle下需要在构建流程中嵌入代码注入的处理,用到了hook task(在已有插件Taskgraph中嵌入自定义task),参考ReactNative的react.gradle脚本。
总结如下:
1.使用命令–dry-run 查看执行的task,判断hook位置(插入点),这样看到的是执行的所有task,github 上有个插件可以看到task依赖树更清楚,可惜插件运行出错
2.自定义task,使用task依赖,在hook位置插入自定义task :selfTask.dependsOn task1 task2.dependsOn selfTask; 要注意的是有的task并不能在脚本中直接作为属性使用,需要在afterEvaluate回调中设置依赖(?)

在研究第二步的问题时在网上找到一篇文章解决hook task问题,文中提到的assembleDebug成功的情况在2.2.2版本插件也是失败的,但在回调中解决的方案是正确的。
原文Android Gradle Learnings: Hooking into Android Tasks 翻译如下:

大部分的安卓开发者都会满足于对gradle,这一我们每天工作都要用到的构建工具,的基础掌握;在最近探索了一下如何自定义Task之后,我觉得有必要分享一下我在学习中的一些收获。(阅读本文需要对gradle构建的基础知识有所掌握)
在android项目gradle构建插件中提供了一个名为lint的task,是的,这个task会对你的代码进行lint/静态代码检查并生成问题报告。
我的目标是在lint这个task中附加一些操作并自定义Task对输出报告进行一些处理,报告位于ProjectRoot/app/build/outputs/lint-results.html
(在task中附加额外操作请参考gradle官方手册)

Attempt 1:

def executePostLint() {
  println 'execute post lint task'
}
lint << {
  executePostLint
}

运行脚本出现了错误,gradle找不到lint属性:

> ./gradlew lint
* What went wrong:
A problem occurred evaluating project ':app'.
> Could not find property 'lint' on project ':app'.

然而,当我尝试在另一个Task assembleDebug中添加操作时,却发现同样的语法在这个task上成功了

def executePostDebugAssemble() {
  println 'Do some stuff'
}
assembleDebug << {
  executePostDebugAssemble
}
assembleDebug 执行了我自己的代码也正常执行了
(... other tasks ommitted for brevity)
:app:assembleDebug
Do some stuff
这是我十分不解,在使用gradle tasks命令查看所有task时,assembleDebug和lint都是存在的,但是为什么可以直接在脚本中使用assembleDebug却不能直接用lint;尽管不确定其原因,lint这个task只有在gradle的evaluate/configuration步骤之后才是可用的。(参考gradle文档 Build Lifecycle Events篇)
为了hook一个动态生成的task(像lint这样的),需要使用afterEvaluate方法
afterEvaluate { project ->
  project.tasks.lint << {
      executePostLint()
  }
}
  事实证明与指定variant/build flavor相关的task都需要使用相同的方式才能使用
// WORKS
assembleFlavor1 << {
  println 'adding step to the end of task!'
}
// DOESN'T WORK... (note Debug)
assembleFlavor1Debug << {
  println 'adding step to the end of task!'
}
// CORRECT
afterEvaluate { project ->
  project.tasks.assembleFlavor1Debug << {
    println 'adding step to the end of task!'
  }
}

自定义Task依赖Android Gradle Task
下面说一下自定义task,并使其依赖已存在的Android插件task,而不是直接在已有的task上追加操作。
比如说,如果希望自定义一个自动上传某个flavor版本app的task,在此之前应该执行一个assembleFalvor1之类的task
虽然可以直接在assemble里添加操作,但这并不是最好的选择,因为你并不希望所有的flavor都执行这个上传操作;更好的方案是自定义一个独立的task,暂且叫它uploadFlavor1Apk,使其依赖assembleFlavor1。
这种方案很简洁,创建task并使其依赖assembleFlavor1

     task iDependOnAssembleFlavor1(dependsOn: assembleFlavor1) << {
  println 'I need assembleFlavor1 to run before me.'
}

使用-m命令执行iDependOnAssembleFlavor1可以看到我们预期的效果

> ./gradlew iDependOnAssembleFlavor1 -m
... other tasks ommitted for brevity
:app:assembleFlavor1 SKIPPED
:app:iDependentOnAssembleFlavor1 SKIPPED

ok,我们继续,我们实际上只需要上传Falvor1的Release版本,只需要生成Release版本,而assembleFlavor1会生成Release和Debug两个版本,在原来的版本上修改一下

task iDependOnAssembleFlavor1Release(dependsOn: assembleFlavor1Release) << {
    println 'I need assembleFlavor1Release to run before me.'
}

好吧,类似之前的lint遇到的问题,又报错了

FAILURE: Build failed with an exception.
* What went wrong:
A problem occurred evaluating project ':app'.
> Could not find property 'assembleFlavor1Release' on project ':app'.

Gradle找不到这个task是因为这个Task是动态生成的,你需要在周期回调中处理

tasks.whenTaskAdded { task ->
  if (task.name == 'assembleFlavor1Release') {
    iDependOnAssembleFlavor1Release.dependsOn(task)
  }
}

whenTaskAdded回调接受一个闭包参数,并在task添加到taskGraph时执行闭包操作,这样就能持有生成的Task并添加依赖
最终实现如下

task iDependOnAssembleFlavor1Release << {
    println 'I need assembleFlavor1Release to run before me.'
}

tasks.whenTaskAdded { task ->
    if (task.name == 'assembleFlavor1Release') {
        iDependOnAssembleFlavor1Release.dependsOn(task)
    }
}

执行结果如下

> ./gradlew iDependOnAssembleFlavor1Release -m

... other tasks ommitted for brevity
:app:assembleFlavor1Release SKIPPED
:app:iDependOnAssembleFlavor1Release SKIPPED

BUILD SUCCESSFUL

你可能感兴趣的:(gradle)