编译失败常见原因:Kotlin/Java版本不一致、AAPT2错误、资源文件问题、NDK编译失败
Kotlin/Java 版本不一致
这个问题在混合语言项目里特别常见。你想想看,Gradle 的 JVM 版本、Kotlin 编译器版本、Java 源码兼容性,这三者只要有一个对不上,编译就崩给你看。我印象最深的一次,团队接了个老项目,用的 Kotlin 1.3,但 CI 环境默认装了 JDK 17…… 结果一堆 "Unsupported class file major version" 的错误,当时排查了好久。
一般遇到这类错误,你会在日志里看到类似这样的信息:
Execution failed for task ':app:compileDebugKotlin'.
> 'compileDebugJavaWithJavac' task (current target is 1.8) and 'compileDebugKotlin' task (current target is 11) jvm target compatibility should be set to the same Java version.
说白了就是 Kotlin 编译的 JVM 目标和 Java 编译的 JVM 目标不一致。我建议你遇到这种情况先做三件事:
- 检查 Gradle JVM 设置 —— 在
gradle.properties里看org.gradle.jvmargs有没有指定 JDK 路径。CI 上经常因为全局环境变量覆盖导致版本错乱。 - 统一 kotlin jvmTarget —— 在项目的
build.gradle里显式声明:
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { kotlinOptions.jvmTarget = "1.8" } - 检查 Kotlin 插件版本 —— 某些旧版 Kotlin 插件不兼容新版 AGP。比如 Kotlin 1.4 配 AGP 7.0 以上就会炸。我一般在
buildscript里锁定版本:
ext.kotlin_version = "1.6.21"
java.toolchain 强制指定 JDK 版本。这样项目级配置不会被服务器环境带偏。
AAPT2 错误
AAPT2 报错最常见的就是“资源链接失败”。我曾经连续两周被这个问题折磨——团队里有人提交了一个命名违规的 .9.png 文件,AI 生成的绘图居然有半透明像素,AAPT2 直接罢工。错误日志一般长这样:
Android resource linking failed
Output: error: resource drawable/ic_launcher_background (aka com.example:drawable/ic_launcher_background) not found.
实际上 AAPT2 把资源编译成二进制格式时,对格式要求非常严格。我归纳了几类高频问题:
- .9.png 裁剪线问题 —— 黑线必须为纯黑(#000000),不能有抗锯齿。工具生成的自动拉伸图,经常带灰色像素。我习惯在提交前用
draw9patch工具手动检查一遍。 - 资源文件名非法字符 —— Android 资源名只允许小写字母、数字、下划线,不能有大写字母或横杠。我见过同事把图片命名为
bg-image.png,AAPT2 立刻报错。这个规则经常被忽略。 - 重复资源定义 —— 多个模块如果定义了相同资源(比如
R.string.app_name),编译时会冲突。最好用resourcePrefix或模块命名空间来隔离。
1. 先看完整日志,找到
error: 后面的具体资源路径。2. 用
aapt2 dump 命令检查编译后的中间文件。3. 如果还不行,清理
build/ 目录后重试——有时候是缓存脏了。
资源文件问题
资源文件问题其实比 AAPT2 范围更广,包括 XML 格式错误、图片资源损坏、尺寸不对等。我遇到过一个典型案例:CI 构建时突然失败,日志显示某个 layout 文件里有 android:layout_gravity 拼写成了 android:gravity,这种肉眼很难看出来,但 IDE 在编译时不会提醒?其实 AS 会提示,但 CI 环境没有实时检查。
资源文件出错的表现五花八门,但本质都是 G/AAPT 在编译过程中解析失败。我建议你关注这几个点:
- XML 标签必须闭合 —— 尤其
<merge>或<include>容易漏掉结尾。 - 图片资源尺寸异常 —— 比如 0x0 的 PNG,或者色深超过 32位的 WebP。CI 可以加个脚本来校验图片合法性。
- 字符串资源里有转义冲突 —— 比如
&必须写成&,我习惯在strings.xml里统一用转义字符。
Android & iOS 都支持
Android & iOS 都支持
NDK 编译失败
NDK 编译失败通常更底层,错误信息也最难读。我记得有一次升级了 NDK 版本,结果 CMakeLists.txt 里引用的预编译库是旧的 .so,ABI 架构不对,链接报了一堆 undefined reference。那段时间我几乎逐行看了 CMake 输出。
常见的 NDK 编译失败场景:
- ABI 架构不匹配 —— 比如
app/build.gradle里配置了ndk.abiFilters 'arm64-v8a', 'x86',但你的 .so 只提供了 armeabi-v7a 版本。解决办法是在jniLibs里放对文件夹,或者统一 ABI 过滤。 - NDK 版本与工具链冲突 —— CMake 或 ndk-build 对不同 NDK 版本的支持不一样。我建议你锁定一个稳定版本,比如 NDK r23c,并且在 CI 脚本里用
ANDROID_NDK_HOME环境变量指定。 - 标准库链接错误 —— 如果你的 C++ 代码用了
<filesystem>,需要链接libc++_shared.so并保证它被打包进 APK。我就在这个坑里爬了整整一个下午。
build.gradle 里开启 android.buildTypes.release.debuggable true,然后用 ndk-gdb 或 readelf 检查 .so 的符号表。或者直接在 Gradle 命令后面加 --info 看详细的 CMake 输出。
说白了,编译失败的根因往往是环境配置、版本对齐、资源合规这三件事。只要你在 CI 脚本里把这些检查自动化,绝大多数问题都能提前被发现。我在项目里写了一个 pre-build 的 check 任务,跑一遍资源格式、JDK 版本、NDK 标准库验证,之后编译失败率降了 70% 以上。