模块划分原则
| 模块类型 | 内容 |
|---|---|
app | 壳工程、导航入口、依赖注入 |
feature | 独立业务(Home、Profile) |
core | 基础能力(network、database、designsystem) |
library | 通用工具、日志、分析等 |
资源管理
1. 命名规范
- 使用前缀区分模块:
feature_home_title。 - 在
core/designsystem中集中管理颜色、字体、尺寸。
object Dimens {
val screenPadding = 16.dp
}
val LocalDimens = staticCompositionLocalOf { Dimens }
2. 共享资源
- 使用
gradleandroidResources { additionalParameters += "--stable-ids" }保证资源 id 稳定。 - 在
core/designsystem导出公共组件与 theme。
路由通信
接口化导航
interface HomeNavigator {
fun openDetail(context: Context, id: Long)
}
class HomeNavigatorImpl @Inject constructor() : HomeNavigator {
override fun openDetail(context: Context, id: Long) {
val intent = Intent(context, DetailActivity::class.java)
intent.putExtra("id", id)
context.startActivity(intent)
}
}
- 在
app模块通过 Hilt 注入具体实现。
Compose 导航模块化
interface NavGraphInstaller {
val route: String
fun install(builder: NavGraphBuilder, navController: NavController)
}
@Module
@InstallIn(SingletonComponent::class)
abstract class NavigationModule {
@Binds @IntoSet
abstract fun bindHomeInstaller(installer: HomeNavInstaller): NavGraphInstaller
}
@Composable
fun AppNavHost(installers: Set<NavGraphInstaller>) {
val navController = rememberNavController()
NavHost(navController, startDestination = "home") {
installers.forEach { it.install(this, navController) }
}
}
依赖管理
- 使用 Version Catalog (
libs) 管理依赖版本。 - 在
build-logic中创建 Convention Plugins。 - Feature 模块只依赖必要的
core模块,避免环依赖。
plugins {
id("com.example.android.feature")
}
dependencies {
implementation(projects.core.designsystem)
implementation(projects.core.model)
}
动态特性模块
- 使用 Play Feature Delivery 或 SplitCompat。
- 路由通过接口访问,模块加载后再反射或
ServiceLoader获取实现。
fun Context.loadFeatureNavigator(): FeatureNavigator? {
return try {
Class.forName("com.example.feature.FeatureNavigatorImpl")
.getDeclaredConstructor()
.newInstance() as FeatureNavigator
} catch (e: ClassNotFoundException) {
null
}
}
常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 资源命名冲突 | 跨模块使用相同资源名 | 引入命名空间前缀,或使用专用资源模块 |
| 依赖环 | 模块互相引用 | 拆分基础模块,通过接口转移依赖关系 |
| 路由更新困难 | 模块直接构造 Intent | 使用接口 + DI 管理路由 |
实战案例:多模块的”甜蜜点”在 3-5 个
多模块听起来很架构,但我见过(也写过)两种反模式:
- 单模块到底:10 万行代码一个
:app,改一行,全量编译 3 分钟。 - 模块狂魔:
:feature-home、:feature-home-api、:feature-home-impl……20 个模块,模块间依赖画成蜘蛛网,改个接口要动 5 个模块。
Google 的 Now in Android 是 10+ 模块的标杆,但那是 50 万行级的项目。我的实战经验是:先按”业务边界”拆 3-5 个(:app、:core-ui、:core-data、:feature-xxx),等编译真的慢了、团队真的并行开发了,再细拆。过早模块化比不模块化更痛苦——Gradle 的模块依赖是编译期强约束,拆错了比重构还贵。
路由侧,ARouter/WMRouter 这些 annotation 路由,本质是 KSP/kapt 生成路由表,和 Flutter 的 go_router + deep_link 一个思路。Flutter 里我直接用 go_router 的 GoRoute(path: '/product/:id'),Kotlin 侧对等的是 Navigation Compose 的 composable("product/{id}")。选型看团队:新项目 Navigation Compose 足够,老项目混着 XML Fragment 就用 ARouter 过渡。
参考:Android 官方文档《Guide to app architecture》;Now in Android 开源项目
总结
多模块工程需要统一的资源命名、路由接口与依赖管理策略。通过设计系统模块、导航接口与 Gradle Convention 插件,可以构建更可维护的 Kotlin Android 项目架构,为规模化团队协作提供保障。
