第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
28.1.2 SDK 版本不匹配
这个错误几乎每个 Android 开发者都遇到过:
Failed to find target with hash string 'android-34'
原因很简单——你本地没装这个版本的 SDK。解决方案:
- 打开 SDK Manager,下载对应版本
- 或者修改
compileSdk为你本地已有的版本
gradle.properties 里声明 SDK 版本,问题才解决。
28.1.3 依赖找不到(Dependency not found)
错误信息类似:
Could not resolve com.example:library:1.0.0
排查步骤:
- 检查仓库配置——是不是漏了
mavenCentral()或google() - 检查版本号——是不是写错了
- 检查网络——公司内网可能需要配置代理
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'
}
}
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 的执行时间
- 依赖关系图
- 构建缓存命中情况
- 错误和警告详情
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 知识体系总览
下面这张图,把本章的核心内容串起来了。你对照着看,思路会更清晰。
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
我的排查步骤:
- 先看日志:用
--info重新跑了一次,发现是values.xml里引用了不存在的资源 ID - 定位模块:用
dependencies检查,发现是某个第三方库的资源冲突 - 解决冲突:在
build.gradle里排除了该库的资源传递依赖 - 验证:重新构建,问题解决