19、构建脚本调试:Gradle日志级别、Stacktrace分析、断点调试Gradle脚本、常见错误排查

说实话,Gradle 脚本写多了,谁还没遇到过几个玄学问题?

我记得刚接手一个大型模块化项目时,构建突然就挂了,日志里就一行「Task failed with an exception」。我当时盯着屏幕看了十分钟,愣是没找到原因。后来才发现,是我压根没打开正确的日志级别。

这一章,我就把调试 Gradle 脚本的看家本事都掏出来。从日志级别到断点调试,再到那些你一定会踩的坑,咱们一个一个过。

19.1 Gradle 日志级别:你的第一道防线

Gradle 的日志系统其实挺贴心的。它分了六个级别,从安静到啰嗦,任你挑选。

日志级别 命令行参数 输出内容
ERROR -q 或 --quiet 只输出错误信息
WARNING -w 或 --warn 错误 + 警告
LIFECYCLE (默认) 构建生命周期关键节点
INFO -i 或 --info 更详细的进度信息
DEBUG -d 或 --debug 所有细节,包括堆栈

我个人习惯,平时用默认的 LIFECYCLE 就够了。但一旦出问题,我第一件事就是加 -i-d

我的小技巧: 别一上来就开 DEBUG。信息量太大,反而找不到重点。先试试 --info,如果还看不出问题,再上 --debug

举个例子,你想看某个 Task 到底有没有被执行:

./gradlew assembleDebug --info | grep "Task :app:"

这样过滤一下,只看你关心的 Task,效率高很多。

19.2 Stacktrace 分析:别怕那堆调用栈

很多同学看到 Stacktrace 就头大。其实它没那么可怕。

Gradle 提供了两种 Stacktrace:

  • 简短模式(-s): 只输出跟 Gradle 脚本相关的调用栈。适合定位脚本语法错误。
  • 完整模式(-S): 输出所有调用栈,包括 Gradle 内部代码。适合定位插件或构建逻辑的深层问题。

我在项目中遇到过最典型的情况:自定义 Task 执行时报 NullPointerException。用 -s 一看,发现是我在 doLast 里引用了一个还没初始化的变量。

核心原则: 先看异常类型,再看「at」后面的行号。别被几十行的「Caused by」吓到,通常最后一个才是真正的根因。

19.3 断点调试 Gradle 脚本:像调试 Java 一样调试 Groovy

嗯,这里要注意。很多人不知道 Gradle 脚本其实是可以打断点的。

你想想看,如果能在 IDE 里一步步跟踪脚本执行,那排查问题的效率能提升多少?

具体做法很简单:

  1. 在 Android Studio 或 IntelliJ IDEA 中,打开你的 build.gradle 文件。
  2. 在行号旁边点击,打上断点。
  3. 在终端执行:./gradlew assembleDebug -Dorg.gradle.debug=true --no-daemon
  4. Gradle 会启动一个 JVM 并等待调试器连接。在 IDE 中创建一个「Remote JVM Debug」配置,端口默认是 5005。
  5. 启动调试,IDE 就会停在你的断点处。
注意: 一定要加 --no-daemon,否则调试器可能连不上。另外,调试的是 Groovy 脚本,不是 Java 代码,所以有些 Java 调试技巧可能不适用。

我曾经用这个方法,花了半小时就定位了一个插件里的资源冲突问题。要是靠加日志打印,估计得折腾一整天。

19.4 常见错误排查:那些年我们踩过的坑

下面这几个错误,我敢说你迟早会遇到。提前了解一下,能省不少时间。

19.4.1 依赖冲突

错误信息通常是:Conflict with dependency ...Duplicate class ...

排查命令:

./gradlew :app:dependencies --configuration debugRuntimeClasspath

这个命令会输出完整的依赖树。你一眼就能看出哪个库被引用了多个版本。

我的经验: 别手动去改版本号。用 force = trueexclude 来统一版本,更干净。

19.4.2 Task 执行顺序错乱

症状:某个 Task 执行时,依赖的产物还没生成。

原因:dependsOnmustRunAfter 没写对。

排查方法:

./gradlew :app:assembleDebug --dry-run

这个命令不会真正执行,只会打印出 Task 的执行顺序。你一看就知道哪里不对。

19.4.3 配置阶段耗时过长

症状:构建卡在「Configuring」阶段很久。

原因:通常是 allprojectssubprojects 里写了太多耗时操作。

排查方法:

./gradlew --profile assembleDebug

这个命令会生成一份 HTML 报告,详细列出每个阶段和每个 Task 的耗时。我上次用这个发现,一个同事在 subprojects 里做了网络请求,直接拖慢了整个构建。

19.4.4 插件版本不兼容

错误信息:Plugin with id 'xxx' not foundCould not find method xxx()

原因:插件版本和 Gradle 版本不匹配。

解决方案:去官方文档查兼容性矩阵。别自己瞎猜。

避坑指南: 我曾经因为升级了 AGP 版本,忘了更新 Kotlin 插件,结果编译了整整两天都没过。后来发现是版本不兼容。从那以后,我每次升级都会先看 release notes。

19.5 知识体系总览

下面这张图,把本章的核心逻辑串起来了。你可以把它当作调试时的检查清单。

Gradle 构建脚本调试知识体系 问题定位 日志分析 断点调试 错误排查 --dry-run --profile dependencies 任务 -q / -w / -i / -d -s / -S grep 过滤 Remote JVM Debug --no-daemon 断点打在 build.gradle 依赖冲突 顺序错乱 版本不兼容 输出结果:构建报告 / 依赖树 / 堆栈信息 / 执行顺序 最终目标:快速定位并修复构建问题

说白了,调试 Gradle 脚本没什么神秘的。就是先定位问题范围,然后选择合适的日志级别或调试工具,最后根据输出信息去排查。多练几次,你也能成为团队里的 Gradle 救火队员。

最后送你一句话: 别怕报错。每一次报错,都是你深入理解构建系统的机会。我到现在还保留着第一次成功调试 Gradle 脚本时的截图呢。

公众号:蓝海资料掘金营,微信 deep3321