项目模块化设计:单一 Activity + 分层架构 + Hilt
这一章我们聊点实在的。你想想看,一个项目如果堆在一起,后期维护就是噩梦。我个人习惯从一开始就做好模块化,至少把架构定死。说白了,就是三个核心问题:用几个 Activity?代码怎么分层?依赖怎么管?
为什么我坚持单一 Activity
我刚开始做 Android 时,喜欢一个页面一个 Activity。后来项目大了,跳转传参、回退栈管理、状态恢复……各种坑。尤其是 Compose 出现后,导航完全交给了 Navigation Compose,那还要多个 Activity 干嘛?
单一 Activity 的好处很明显:
- 生命周期集中管理 —— 你只用关心 Application 和 Activity 的生命周期。
- 模块间解耦 —— 每个页面只是一个 Composable,可以独立测试。
- 状态恢复更简单 —— SavedStateHandle 在 ViewModel 里直接可用。
分层架构:UI / Data / Domain
好,现在我们把代码切三块。这不是什么新概念,但真的管用。我见过很多新手把网络请求、数据库查询、UI 逻辑全塞在一个 ViewModel 里,后来改需求改到崩溃。分层就是强制你遵守“各司其职”。
| 层级 | 职责 | 典型类 | 依赖关系 |
|---|---|---|---|
| UI Layer | 展示数据、处理用户交互 | Activity, Composable, ViewModel | 依赖 Domain Layer |
| Domain Layer | 业务逻辑、用例编排 | UseCase, 纯 Kotlin 类 | 依赖 Data Layer(接口) |
| Data Layer | 数据源实现(本地/远程) | Repository, DAO, Retrofit | 无上层依赖 |
这里有个关键点:Domain 层只依赖接口,不依赖具体实现。比如 UseCase 里用 Repository 接口,而不是 Room 的 DAO。这样以后切换数据源,只用改 Data 层,Domain 层完全不用动。
Hilt 依赖注入:别让自己写工厂
依赖注入不是新鲜事,但很多新手嫌麻烦。我一开始也觉得“手动 new 不就得了”,直到项目大起来,构造参数变得越来越多,改一个参数要改 N 个地方。Hilt 就是帮我们自动做这件事。
引入 Hilt 只需三步:
- 在 build.gradle 添加 Hilt 插件和依赖。
- 在 Application 类上添加
@HiltAndroidApp。 - 在 Activity 上添加
@AndroidEntryPoint。
代码示例(Application):
@HiltAndroidApp
class MyApplication : Application()
Activity 中注入 ViewModel:
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
// Hilt 会自动提供 ViewModel
private val viewModel: MyViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyAppTheme {
MyScreen(viewModel)
}
}
}
}
ViewModel 需要标记 @HiltViewModel:
@HiltViewModel
class MyViewModel @Inject constructor(
private val myUseCase: MyUseCase
) : ViewModel()
这样一来,UseCase 又是通过构造函数注入。只要在 Hilt Module 里告诉它怎么提供 Repository,整个链条就自动串起来了。
完整分层图:一眼看清依赖关系
下面这张图把咱们刚才说的三个层和 Hilt 连线画在一起。你可以看到各个模块怎么协作,Hilt 怎么把依赖从 Data 层注入到 Domain 层,再注入到 UI 层。
嗯,这张图应该能帮你理清思路。UI 层只依赖 Domain 层的接口(通过 ViewModel 引用 UseCase),Domain 层依赖 Data 层的接口(Repository),Data 层负责具体数据库实现。Hilt 像个幕后管家,告诉各模块“你需要什么,我给你”。
实践中的避坑指南
我刚才说分层依赖接口,但很多新手会直接在 Domain 层引入 Room 的注解。**千万别**。Domain 层应该是纯 Kotlin,不依赖任何 Android 框架。我曾经在一个项目里犯过这个错,导致 Domain 层没法做单元测试(因为需要运行在 Android 环境)。
另外,Hilt 的 Module 要记得加 @InstallIn(SingletonComponent::class)。我自己以前漏掉过,结果 Hilt 抱怨找不到依赖,排查了半天。
好,就到这吧。下节我们真正开始写 Room 的数据库和 DAO。