核心理念 :状态驱动UI + 单向数据流
关键组件:ViewModel + MutableState/StateFlow + Composable函数
一、MVVM 在 Compose 中的分层实现
1. Model 层 - 数据抽象层
Model≡数据源抽象
职责与实现:
kotlin
kotlin
复制
// Repository 数据仓库
class UserRepository {
// 网络数据源
suspend fun fetchUsers(): List<User> {
return apiService.getUsers()
}
// 本地数据源
suspend fun saveUsers(users: List<User>) {
userDao.insertAll(users)
}
}
核心特性:
- 统一提供数据获取接口
- 封装数据源实现细节(网络/数据库)
- 对 ViewModel 提供原子操作
2. ViewModel 层 - 状态管理中心
核心职责:
- 托管UI状态
- 处理业务逻辑
- 转换Model数据为UI可消费状态
典型实现:
kotlin
kotlin
复制
class UserViewModel(
private val repository: UserRepository
) : ViewModel() {
// UI状态托管
private val _uiState = mutableStateOf<UserListState>(UserListState.Loading)
val uiState: State<UserListState> = _uiState
// 事件处理
fun loadUsers() {
viewModelScope.launch {
_uiState.value = UserListState.Loading
try {
val users = repository.fetchUsers()
_uiState.value = UserListState.Success(users)
} catch (e: Exception) {
_uiState.value = UserListState.Error(e.message)
}
}
}
// 封装单次事件(Toast/导航)
private val _navigateEvent = Channel<Destination>()
val navigateEvent = _navigateEvent.receiveAsFlow()
}
关键机制:
viewModelScope
自动管理协程生命周期- 使用
mutableStateOf
/StateFlow
托管状态 - 将复杂操作分解为原子函数
3. View 层(Compose UI)- 声明式UI
响应式实现:
kotlin
kotlin
复制
@Composable
fun UserListScreen(viewModel: UserViewModel = hiltViewModel()) {
val uiState by viewModel.uiState.collectAsState()
// 监听单次事件
LaunchedEffect(Unit) {
viewModel.navigateEvent.collect { destination ->
navController.navigate(destination.route)
}
}
// 状态驱动UI
when (val state = uiState) {
is UserListState.Loading -> LoadingView()
is UserListState.Success -> UserListView(users = state.users,
onItemClick = { user ->
viewModel.onUserSelected(user) // 事件传递
}
)
is UserListState.Error -> ErrorView(retry = viewModel::loadUsers)
}
}
状态重组原则:
图片代码
状态变化
触发重组
仅更新受影响组件
二、数据流向与核心机制
1. 单向数据流模型
UI事件函数调用ViewModel操作Model
Model数据处理ViewModel新状态UI
2. 状态管理最佳实践
状态提升策略:
kotlin
kotlin
复制
@Composable
fun SearchScreen() {
var searchQuery by remember { mutableStateOf("") }
// 状态提升 - 子组件通过回调修改状态
SearchBar(searchQuery = searchQuery, onQueryChange = { searchQuery = it })
SearchResults(query = searchQuery)
}
状态分类标准:
状态类型 | 作用域 | 存储位置 | 使用场景 |
---|---|---|---|
UI局部状态 | 单个Composable | remember { mutableStateOf() } |
动画进度/临时输入 |
业务全局状态 | 多个屏幕共享 | ViewModel | 用户数据/应用设置 |
路由状态 | 整个应用 | NavHostController |
导航状态管理 |
三、常见问题与优化策略
1. 状态管理陷阱
问题:重组时状态初始化
kotlin
kotlin
复制
@Composable
fun Counter() {
var count = 0 // 每次重组重置为0
Button(onClick = { count++ }) {
Text("Count: $count")
}
}
解决方案 :使用remember
缓存状态
csharp
kotlin
复制
var count by remember { mutableStateOf(0) }
2. 单次事件处理
问题:LiveData在配置变更后重复触发事件
解决方案:使用Channel或SharedFlow
kotlin
kotlin
复制
// ViewModel
private val _toastEvent = Channel<String>()
val toastEvent = _toastEvent.receiveAsFlow()
fun showMessage(msg: String) {
viewModelScope.launch {
_toastEvent.send(msg)
}
}
// Compose
LaunchedEffect(Unit) {
viewModel.toastEvent.collect { toastMessage ->
Toast.makeText(context, toastMessage, Toast.LENGTH_SHORT).show()
}
}
3. 性能优化方案
-
重组控制:
scsskotlin 复制 LazyColumn { items(users, key = { it.id }) { user -> // 使用key提升性能 UserItem(user) } }
-
派生状态优化:
csharpkotlin 复制 val filteredUsers by remember(users, searchQuery) { derivedStateOf { users.filter { it.name.contains(searchQuery, true) } } }
-
副作用隔离:
scsskotlin 复制 LaunchedEffect(userId) { // 限定副作用触发条件 loadUserDetails(userId) }
四、MVVM与其他架构对比
特性 | MVVM | MVI | MVP |
---|---|---|---|
状态管理 | 多状态分散 | 单状态集中 | 状态与视图耦合 |
数据流方向 | 双向绑定 | 严格的单向流 | 手动同步 |
Compose适配性 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐ |
学习曲线 | 中等 | 较陡峭 | 简单 |
典型场景 | 常规CRUD应用 | 复杂状态应用 | 遗留项目改造 |
推荐组合 | ViewModel + StateFlow | ViewModel + MVI架构组件 | Presenter + 回调接口 |
五、最佳实践总结
-
分层原则:
- 保持View层无业务逻辑
- ViewModel只暴露UI所需状态
- Model层负责纯粹数据操作
-
状态管理铁律:
- UI始终是状态的函数:UI=f(state)
- 使用不可变数据类表示状态
kotlinkotlin 复制 data class UserListState( val isLoading: Boolean = false, val users: List<User> = emptyList(), val error: String? = null )
-
混合架构策略:
kotlinkotlin 复制 // MVVM + MVI混合实践 class UserViewModel : ViewModel() { private val _state = MutableStateFlow(UserListViewState()) val state: StateFlow<UserListViewState> = _state.asStateFlow() fun onEvent(event: UserListEvent) { when (event) { is UserListEvent.Load -> fetchUsers() is UserListEvent.SelectUser -> selectUser(event.user) } } }
-
测试策略:
- ViewModel:使用JUnit进行业务逻辑测试
- Composable:使用
ComposeTestRule
进行UI行为验证 - 模块间:使用接口隔离,通过Mock测试协作
文档附件 :Jetpack Compose MVVM实战项目模板(github.com/android/com...)
进阶学习 :Compose状态管理官方指南(developer.android.com/jetpack/com...)