Jetpack Compose 中的 MVVM 架构解析

核心理念 ​:状态驱动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. 性能优化方案

  1. 重组控制​:

    scss 复制代码
    kotlin
    复制
    LazyColumn {
        items(users, key = { it.id }) { user -> // 使用key提升性能
            UserItem(user)
        }
    }
  2. 派生状态优化​:

    csharp 复制代码
    kotlin
    复制
    val filteredUsers by remember(users, searchQuery) {
        derivedStateOf {
            users.filter { it.name.contains(searchQuery, true) }
        }
    }
  3. 副作用隔离​:

    scss 复制代码
    kotlin
    复制
    LaunchedEffect(userId) { // 限定副作用触发条件
        loadUserDetails(userId)
    }

四、MVVM与其他架构对比

特性 MVVM MVI MVP
状态管理 多状态分散 单状态集中 状态与视图耦合
数据流方向 双向绑定 严格的单向流 手动同步
Compose适配性 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐
学习曲线 中等 较陡峭 简单
典型场景 常规CRUD应用 复杂状态应用 遗留项目改造
推荐组合 ViewModel + StateFlow ViewModel + MVI架构组件 Presenter + 回调接口

五、最佳实践总结

  1. 分层原则​:

    • 保持View层无业务逻辑
    • ViewModel只暴露UI所需状态
    • Model层负责纯粹数据操作
  2. 状态管理铁律​:

    • UI始终是状态的函数:UI=f(state)
    • 使用不可变数据类表示状态
    kotlin 复制代码
    kotlin
    复制
    data class UserListState(
        val isLoading: Boolean = false,
        val users: List<User> = emptyList(),
        val error: String? = null
    )
  3. 混合架构策略​:

    kotlin 复制代码
    kotlin
    复制
    // 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)
            }
        }
    }
  4. 测试策略​:

    • ViewModel:使用JUnit进行业务逻辑测试
    • Composable:使用ComposeTestRule进行UI行为验证
    • 模块间:使用接口隔离,通过Mock测试协作

文档附件 ​:Jetpack Compose MVVM实战项目模板(github.com/android/com...)

进阶学习 ​:Compose状态管理官方指南(developer.android.com/jetpack/com...)

相关推荐
Dolphin_海豚11 分钟前
electron windows 无边框窗口最大化时的隐藏边框问题
前端·electron·api
梦想CAD控件14 分钟前
WEB CAD与Mapbox结合实现在线地图和CAD编辑(CGCS2000)
前端·javascript·vue.js
AverageJoe19911 小时前
一次vite热更新不生效问题排查
前端·debug·vite
努力只为躺平1 小时前
🔥 油猴脚本开发指南:从基础API到发布全流程
前端·javascript
bitbitDown1 小时前
我用Playwright爬了掘金热榜,发现了这些有趣的秘密... 🕵️‍♂️
前端·javascript·vue.js
陈随易1 小时前
VSCode v1.102发布,AI体验大幅提升
前端·后端·程序员
ma771 小时前
JavaScript 获取短链接原始地址的解决方案
前端
该用户已不存在1 小时前
关于我把Mac Mini托管到机房,后续来了,还有更多玩法
服务器·前端·mac
tianchang1 小时前
SSR 深度解析:从原理到实践的完整指南
前端·vue.js·设计模式