管理模块依赖
保持依赖图清晰且无环
管理模块依赖 是 CoddyKit 上的免费 Android Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Android Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Android Academy 课程共包含 4 节课。
依赖图
每个模块都会声明它所依赖的其他模块。这些依赖合在一起就构成了依赖图。健康的依赖图是DAG(有向无环图):依赖只沿一个方向指向,并且永远不会形成循环。
在本课中,您将学习如何整洁地声明依赖、选择正确的 Gradle 配置、共享版本并避免循环依赖。
声明模块依赖
您可以在 dependencies 代码块中使用 project(":path:to:module") 添加对另一个模块的依赖。该路径与文件夹结构一致,并且与您在 settings.gradle.kts 中声明的路径相匹配。
// feature/profile/build.gradle.kts
dependencies {
implementation(project(":core:data"))
implementation(project(":core:designsystem"))
implementation(project(":core:model"))
}implementation 与 api
您选择的配置决定了哪些内容会泄漏给使用者:
implementation:依赖是私有的。依赖于您的模块无法看到它。这是默认选择。api:依赖会被重新公开(传递依赖)。仅当您的公开类型来自该依赖时才使用它。
几乎总是优先使用 implementation——这可以提高构建速度,因为隐藏依赖发生更改时,不会强制使用者重新编译。
// core/data/build.gradle.kts
dependencies {
// Repository signatures return :core:model types,
// so consumers need to SEE it -> api
api(project(":core:model"))
// Network is an internal detail -> implementation
implementation(project(":core:network"))
}implementation 为何能加快构建
使用 implementation 时,Gradle 知道隐藏依赖的更改不会影响模块的公开 ABI。因此,使用者无需重新编译。使用 api 时,更改会沿着传递依赖链影响每一个使用者。
经验法则是:只有当依赖出现在模块的公开类型中(返回类型、公开参数)时,才将它放入 api。否则请使用 implementation。
// Public -> needs api
fun observeUser(): Flow<User> // Flow and User leak out
// Internal -> implementation is enough
private val client: OkHttpClient // never exposed集中管理版本:版本目录
当模块很多时,您不会希望在各处重复填写库版本。Gradle 的版本目录(gradle/libs.versions.toml)可以统一定义版本和别名。每个模块都引用同一个别名。
# gradle/libs.versions.toml
[versions]
compose-bom = "2024.09.00"
retrofit = "2.11.0"
[libraries]
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }在模块中使用版本目录
模块随后通过生成的 libs 访问器引用库。构建文件中不再包含版本号,因此升级只需在一个地方进行。
// core/network/build.gradle.kts
dependencies {
implementation(platform(libs.compose.bom))
implementation(libs.retrofit)
}大忌:循环依赖
当模块 A 依赖 B,而 B 又直接或通过一系列依赖返回依赖 A 时,就形成了循环。Gradle 会拒绝构建循环依赖。它也说明设计存在问题:这两个模块之间的边界划分不正确。
// :feature:cart -> implementation(project(":feature:checkout"))
// :feature:checkout -> implementation(project(":feature:cart"))
//
// Gradle error:
// Circular dependency between the following tasks:
// :feature:cart:compile -> :feature:checkout:compile -> :feature:cart:compile打破循环依赖
要打破循环依赖,请将共享部分提取到一个更低层的模块中,让两个模块都依赖它。如果两个功能需要彼此的数据,那么共享契约应放在核心模块中,而不是放在任一功能模块中。
这样就能恢复向下的依赖流:两个功能都指向核心模块,而核心模块永远不会反向指向上层。
// Before: cart <-> checkout (cycle)
// After: cart -> :core:order <- checkout
// core/order/OrderContract.kt
data class Order(val items: List<CartItem>, val total: Double)
// feature/cart -> implementation(project(":core:order"))
// feature/checkout-> implementation(project(":core:order"))依赖反转:依赖抽象
有时,低层模块需要的行为位于更高层。此时不要向上依赖,而应在低层模块中定义一个接口,再让高层模块通过依赖注入提供实现。这就是依赖反转。
// core/analytics defines the contract
interface AnalyticsLogger {
fun log(event: String)
}
// :app provides the real implementation and injects it down
@Module
@InstallIn(SingletonComponent::class)
object AnalyticsModule {
@Provides
fun logger(impl: FirebaseAnalyticsLogger): AnalyticsLogger = impl
}可视化并守护依赖图
您可以让 Gradle 打印或渲染模块依赖图,甚至添加一个构建失败的测试,以便在出现被禁止的依赖时终止构建(例如核心模块依赖功能模块)。module-graph 等工具可以自动生成图表。
# Print the project structure
./gradlew projects
# Inspect why :feature:home pulls in a library
./gradlew :feature:home:dependencies --configuration debugRuntimeClasspath整洁的无环示例
下面是一个健康的依赖图。请从上到下阅读;没有任何箭头会指回上层,也没有两个模块会彼此指向。这正是您应当实现的结构。
// :app
// -> :feature:home -> :core:data -> :core:network -> :core:model
// -> :feature:profile -> :core:data -> :core:database -> :core:model
// -> :core:designsystem
//
// Every path ends at :core:model. No cycles. Builds in parallel.快速检查
您的 :core:network 模块仅在内部使用 OkHttpClient——它从未出现在任何公开函数签名中。声明 OkHttp 依赖时应使用哪种 Gradle 配置?
回顾:管理模块依赖
您已经学会如何保持模块依赖图健康:
- 使用
project(":path")声明模块依赖。 - 默认使用
implementation;只有公开接口中包含相关类型时才使用api。 - 在版本目录(
libs.versions.toml)中集中管理版本。 - 永远不要创建循环依赖——Gradle 会拒绝它们;可以通过向下提取共享代码,或使用接口进行依赖反转来打破循环。
接下来,您将通过导航连接各个功能,同时避免让它们彼此耦合。
常见问题解答
「管理模块依赖」课时是免费的吗?
是的 — 「管理模块依赖」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Android Academy 课程的其余内容,请升级到 CoddyKit PRO。 Android Academy 课程共包含 4 节课。
「管理模块依赖」这节课中我会学到什么?
保持依赖图清晰且无环 你通过在浏览器中直接运行的动手代码来练习 Android Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Android Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Android Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「管理模块依赖」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Android Academy 课中编写并运行代码吗?
能。每节 Android Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。