子模块编译失败大概率是api/implementation用反了:implementation依赖不传递,仅当前模块可见;若类型出现在public API(如方法参数、返回值、继承类)中,必须用api,否则下游模块编译期找不到该类。

子模块编译失败却找不到原因,大概率是api/implementation用反了
Gradle 多模块项目里,一个子模块(比如 common-utils)被其他模块(比如 app 或 feature-login)依赖时,如果 app 编译报错说“找不到 com.google.gson.Gson”,但你在 common-utils 的 build.gradle 里明明写了 implementation 'com.google.code.gson:gson:2.10.1'——这说明问题不在没加依赖,而在于依赖没“透出去”。
根本原因就一条:implementation 声明的依赖只对当前模块可见;它不会出现在该模块生成的编译类路径(compile classpath)中,也不会写进发布后的 pom.xml 或 module.json。下游模块在编译期完全“看不见”它。
什么时候必须用api而不是implementation
不是看“这个库重不重要”,而是看“它的类型是否出现在你模块的 public API 里”。
common-utils提供了一个公共方法:public static User parseJson(String json, Class<user> cls)</user>,内部用了Gson
→cls是调用方传入的,User类型来自外部,但Gson的类型(如TypeToken、JsonDeserializer)如果出现在方法签名或 public 类的字段/继承关系中,就必须用apinetwork-core暴露了ApiResponse<t></t>,而这个泛型类继承自Retrofit.Response<t></t>
→Retrofit.Response是 public API 的一部分,必须用api 'com.squareup.retrofit2:retrofit:2.9.0'你写了
public interface DataRepository extends RoomDatabase
→RoomDatabase来自androidx.room:room-runtime,只要它出现在接口定义里,就得用api,否则下游模块连编译都过不去
implementation误标为api的真实代价
这不是“写错一行配置”的小问题,而是会扩散成构建链路上的雪崩:
-
app模块每次编译都要重新解析所有api依赖的 transitive 依赖树,哪怕只改了一行日志 - 本地修改
utils模块里的私有工具方法,feature-cart和feature-profile也会被强制重新编译(因为它们依赖了utils的api依赖) - 发布到 Maven 仓库后,下游项目 pom.xml 里会多出一堆本不该出现的
<scope>compile</scope>依赖,比如把postgresql驱动塞进一个纯前端 SDK 的依赖列表 - 更隐蔽的是 IDE 行为:Android Studio 可能缓存了错误的 classpath,导致“明明改了
api却还报找不到类”,clean + rebuild 也不管用,必须 invalidate caches
怎么快速检查自己有没有用错
最直接的办法是看模块产出物:
- 运行
./gradlew :common-utils:generateMetadata(Java Library 插件)或./gradlew :common-utils:publishToMavenLocal - 打开
~/.m2/repository/xxx/common-utils/1.0/common-utils-1.0.pom - 搜索你关心的依赖,比如
gson:- 如果是
<scope>compile</scope>→ 它被声明为api - 如果是
<scope>runtime</scope>或压根没出现 → 它是implementation(或被优化掉了)
- 如果是
另一个验证方式是临时在下游模块写一行代码:
import com.google.gson.Gson; // 如果这行不报红,说明 Gson 已被传递 → 你用了 api
真正容易被忽略的点是:API 边界不是由你“想不想暴露”决定的,而是由你写的 public 方法签名、继承关系、注解处理器输入等客观语法结构决定的。哪怕你主观上认为“这只是个内部工具”,只要它的类型泄漏到了 public 接口里,Gradle 就必须把它当作 API 依赖处理。


















