第28章 Gradle Troubleshooting:常见错误与解决方案

说实话,Gradle 构建出问题的时候,是最让人头疼的。我见过不少团队,代码写得挺好,结果卡在构建上,一卡就是半天。这一章,我就把这些年踩过的坑、积累的经验,系统地梳理一遍。你跟着走一遍,以后遇到问题心里就有底了。

28.1 常见错误与解决方案

先说说那些高频出现的错误。我按出现频率排个序,你对照着看。

28.1.1 内存不足(OutOfMemoryError)

这个我太熟了。早期项目模块少还好,后来拆成几十个模块,构建时直接 OOM。错误信息长这样:

# 典型错误
java.lang.OutOfMemoryError: GC overhead limit exceeded
# 或者
java.lang.OutOfMemoryError: Java heap space

解决方案:

  • gradle.properties 里调大 JVM 内存:org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=512m
  • 开启并行编译:org.gradle.parallel=true
  • 启用构建缓存:org.gradle.caching=true
我的经验:别一上来就给 8G。我习惯先给 4G,观察一段时间,不够再加。给太多反而会让 GC 停顿变长。

28.1.2 SDK 版本不匹配

这个错误几乎每个 Android 开发者都遇到过:

Failed to find target with hash string 'android-34'

原因很简单——你本地没装这个版本的 SDK。解决方案:

  • 打开 SDK Manager,下载对应版本
  • 或者修改 compileSdk 为你本地已有的版本
注意:我曾经因为团队里有人本地 SDK 版本不一致,导致 CI 构建失败。后来统一在 gradle.properties 里声明 SDK 版本,问题才解决。

28.1.3 依赖找不到(Dependency not found)

错误信息类似:

Could not resolve com.example:library:1.0.0

排查步骤:

  1. 检查仓库配置——是不是漏了 mavenCentral()google()
  2. 检查版本号——是不是写错了
  3. 检查网络——公司内网可能需要配置代理

28.2 依赖冲突解决

依赖冲突是 Gradle 构建中最常见的坑之一。说白了,就是同一个库的不同版本被同时引入了。

28.2.1 如何发现冲突

我一般用这个命令:

./gradlew :app:dependencies --configuration releaseRuntimeClasspath

输出结果里,你会看到类似这样的标记:

com.squareup.okhttp3:okhttp:4.9.0 -> 4.11.0

箭头表示 Gradle 自动选择了 4.11.0 版本。但有时候它选的不对,就会出问题。

28.2.2 强制指定版本

当你确定某个版本没问题时,可以强制指定:

configurations.all {
    resolutionStrategy {
        force 'com.squareup.okhttp3:okhttp:4.11.0'
    }
}
避坑指南:我曾经强制指定了一个旧版本,结果导致另一个库的 API 调用失败。所以强制指定前,一定要确认所有依赖方都兼容。

28.2.3 排除传递依赖

有时候你只需要排除某个库的特定传递依赖:

implementation('com.example:library:1.0.0') {
    exclude group: 'com.squareup.okhttp3', module: 'okhttp'
}

28.3 Task 执行失败分析

Task 执行失败,说白了就是 Gradle 在执行某个任务时出了异常。最常见的几个:

28.3.1 编译失败(compileDebugKotlin 失败)

错误信息通常包含具体的 Kotlin 编译错误。排查思路:

  • 先看错误栈顶——那里才是真正的原因
  • 检查 Kotlin 版本是否与 AGP 版本兼容
  • 检查是否有语法错误或类型不匹配

28.3.2 资源合并失败(mergeDebugResources 失败)

这个我遇到过好几次。原因通常是:

  • 资源文件命名冲突(比如两个模块都有 ic_launcher.xml
  • 资源文件格式错误(比如 XML 里标签没闭合)

解决方案:

// 在 build.gradle 中启用资源冲突检查
android {
    aaptOptions {
        cruncherEnabled = false
    }
}

28.4 构建日志分析

构建日志是排查问题的第一手资料。但很多人不会看。

28.4.1 开启详细日志

./gradlew assembleDebug --info
# 或者更详细的
./gradlew assembleDebug --debug

--info 会输出每个 Task 的执行状态,--debug 会输出所有细节。我一般先用 --info,定位到问题 Task 后再用 --debug

28.4.2 分析构建耗时

--profile 参数可以生成一份 HTML 报告:

./gradlew assembleDebug --profile

报告会告诉你每个 Task 花了多少时间。我见过一个项目,光 transformClassesWithDexBuilder 就占了 40% 的时间,后来通过开启混淆和缩减代码,把时间降了一半。

28.5 Gradle 官方调试工具

Gradle 官方提供了一些调试工具,我用得最多的是这几个:

28.5.1 Build Scan(构建扫描)

这是我最推荐的调试工具。用法很简单:

./gradlew assembleDebug --scan

执行完后,终端会输出一个 URL,打开就能看到完整的构建报告。包括:

  • 每个 Task 的执行时间
  • 依赖关系图
  • 构建缓存命中情况
  • 错误和警告详情
小技巧:Build Scan 是免费的,但需要联网。如果你在内网环境,可以用 Gradle Enterprise 搭建自己的扫描服务。

28.5.2 Gradle Daemon 调试

有时候 Daemon 会出问题,比如内存泄漏。重启 Daemon 可以解决大部分问题:

./gradlew --stop
./gradlew assembleDebug

如果问题依旧,可以检查 Daemon 日志:

# 日志位置
~/.gradle/daemon/<version>/daemon-<pid>.out.log

28.5.3 依赖分析工具

除了 dependencies 任务,Gradle 还提供了 dependencyInsight

./gradlew :app:dependencyInsight --dependency okhttp --configuration releaseRuntimeClasspath

这个命令会告诉你某个依赖为什么会被引入,以及最终选择了哪个版本。

28.6 知识体系总览

下面这张图,把本章的核心内容串起来了。你对照着看,思路会更清晰。

Gradle Troubleshooting 知识体系 常见错误 • 内存不足 OOM • SDK 版本不匹配 • 依赖找不到 • 编译/资源合并失败 依赖冲突解决 • 发现冲突 • 强制指定版本 • 排除传递依赖 • 统一版本管理 Task 执行失败 • 编译失败分析 • 资源合并失败 • Task 依赖错误 • 增量编译问题 构建日志分析 • --info / --debug • --profile 耗时分析 官方调试工具 • Build Scan • Daemon 调试 核心原则 1. 先看日志,再动手 2. 定位到具体 Task 3. 善用官方工具

28.7 实战:一次完整的排查过程

最后,我分享一个真实的排查案例。有一次,项目构建突然报错:

Execution failed for task ':app:mergeDebugResources'.
> A failure occurred while executing com.android.build.gradle.internal.res.LinkApplicationAndroidResourcesTask$TaskAction
> Android resource linking failed

我的排查步骤:

  1. 先看日志:用 --info 重新跑了一次,发现是 values.xml 里引用了不存在的资源 ID
  2. 定位模块:用 dependencies 检查,发现是某个第三方库的资源冲突
  3. 解决冲突:在 build.gradle 里排除了该库的资源传递依赖
  4. 验证:重新构建,问题解决
总结一句话:Gradle 调试没那么玄乎。你只要掌握「看日志 → 定位 Task → 分析原因 → 解决」这个流程,大部分问题都能搞定。

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