DEBUG.WIKI
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
雨云;给项目一台云服务器
RackNerd;$21.99/年 · 1GB KVM

AGP 8 报 Namespace not specified:app 已配置,为什么仍然构建失败

AGP 8 提示 Namespace not specified 时,先定位失败模块,再区分 namespace、applicationId 和 manifest package,持久修复本地库与第三方插件配置。

升级 Android Gradle Plugin 后,同步或构建停在 Namespace not specified。打开 app/build.gradle.kts,明明已经写了 namespace,继续在那里加一次也没用。此时先看错误前面的项目路径:缺配置的可能是 :feature:reader、某个本地库,或插件带进来的 Android 模块。

本文讨论 AGP 8.0 起要求模块显式声明 namespace 的构建问题。namespace DSL 从 AGP 7.3 已可使用;AGP 版本和 Gradle Wrapper 版本是两回事,不能看到 Gradle 8 就按字符串判断是否需要迁移。

先定位缺配置的模块

展开构建输出中的第一处配置失败,例如:

A problem occurred configuring project ':feature:reader'.
Namespace not specified. Specify a namespace in the module's build file.

这里要改的是 :feature:reader 对应的模块构建脚本。项目默认目录可能是 feature/reader/build.gradle.kts,但应以 settings.gradle.kts 中的 include(...)、projectDir 或插件配置实际指向的位置为准。

app 模块的 namespace 不会自动替所有依赖模块补上。多模块项目里,只看 app 的配置容易错过本地 Android 库;Flutter、React Native、Capacitor 项目还可能引入由依赖包维护的 Android 模块。先记录失败路径及所属依赖版本,再决定改自己的代码还是升级依赖。

顶层构建脚本通常只声明插件版本,没有该模块的 android 扩展。也不要把 namespace 写到 defaultConfig 内。它是模块 android 块的属性。

在自己的 Android 模块中显式设置

假设失败的是自己维护的 reader 库,历史代码的基础包名是 com.example.reader,模块原有配置可补成:

// feature/reader/build.gradle.kts
plugins {
    id("com.android.library")
    id("org.jetbrains.kotlin.android")
}

android {
    namespace = "com.example.reader"
    // 保留该模块原有的 compileSdk、defaultConfig 等配置
}

这是对现有模块的配置补丁,插件版本仍由项目原来的机制提供。com.example.reader 是示例值,应换成该库实际使用的基础包名;不要把所有模块都填成 app 的包名。

使用 Groovy DSL 的对应位置是:

android {
    namespace 'com.example.reader'
}

旧项目可以从该模块源码的包声明、原 manifest 的 package 和原来引用的 R 包名交叉确认迁移值。namespace 决定生成的 R 类所在包;启用了 BuildConfig 生成时,它也使用这个包。它不会自动重写源码里的 package 或修正错误的 import。

若要从旧 manifest 迁移,可使用 AGP Upgrade Assistant 并查看改动差异。AGP 8 要求在构建脚本声明 namespace,仅在源码 AndroidManifest.xml 保留 package 不能代替这项配置;旧的 package 声明还可能产生另一条迁移错误。只处理确认属于该模块的属性,保留 manifest 组件、权限及其他配置。

namespace 和 applicationId 要分别保留

应用模块可以显式保留两者,例如:

android {
    namespace = "com.example.reader"

    defaultConfig {
        applicationId = "com.example.reader.app"
        // 其余现有配置保持原值
    }
}

这里 namespace 用于代码生成,applicationId 用于识别安装到设备和发布到商店的应用。Android 库模块不需要为修复 namespace 错误添加 applicationId。

对已经发布的应用,保留原 applicationId 和签名关系。不要删除 applicationId,让它因 namespace 的变化而隐式改变。最终合并 manifest 的 package 表示应用 ID,不能看到它与 namespace 不同,就认定构建结果有错。

如果迁移后出现 Unresolved reference: R,核对引用是否仍指向旧包、是否误导入 android.R,以及资源是否另有编译错误。只有确实改了 namespace 才需要调整相应 import;不要为了让 import 暂时通过反复改应用 ID。

第三方插件缺 namespace 时,修依赖的持久版本

若失败模块来自依赖包,先查看该依赖是否有支持当前 AGP 的版本,并连同它对框架、Kotlin 和 Gradle 的要求一起评估。升级后确认锁文件和实际解析版本都已更新,不能只改版本字符串就认为构建一定使用了新版本。

没有可用版本而必须保留依赖时,可以维护范围明确的补丁或分支,为准确的模块设置正确 namespace,并把补丁纳入依赖安装流程。直接编辑 node_modules、Pub 缓存或下载缓存,重新安装后可能丢失改动,不适合作为团队及 CI 的最终修复。

不要用全局 subprojects { afterEvaluate { ... } } 给所有模块随意套上 project.group、目录名或临时包名。不同库的生成类有自己的包关系;没有 namespace 的事实,并不能证明这些推导值正确。全局覆盖还会让后续模块升级和配置顺序更难判断。

若整个依赖栈暂时不支持目标 AGP,回退应恢复上一次可构建的 AGP、Wrapper 及相关插件组合,而不是只把某一个版本号降回去。保留此次修改和锁文件差异,便于后续按依赖升级范围重新迁移。

同步通过后,还要检查资源与测试

从原来失败的 IDE、命令行或 CI 入口重新构建同一变体,确认所有 Android 模块都能完成配置,生成的 R 包名与代码引用一致,再检查应用最终 ID 仍为预期值。

有 instrumentation 测试的模块还要核对 testNamespace。主 namespace 与测试 namespace 不应相同;改测试包后,代码能编译也不代表测试资源引用一定正确。运行原有设备测试,确认 app 与 androidTest 中同名资源没有被错误解析。

如果错误路径已经换成另一个模块,就继续修那个模块;如果变成资源编译、插件兼容或测试运行错误,按新错误处理。Namespace not specified 消失只说明模块命名配置已补齐,不能覆盖其余迁移问题。