Skip to content
阅读进度

怎样写一个能解释清楚的 MVVM 示例? ​

返回 架构与 SDK 目录

标准回答 ​

状态只从 ViewModel 对外流出,UI 通过明确方法提交操作。下面用设备列表演示:阻止同时刷新,单独传播取消,错误转换为可显示状态,页面只负责渲染。Device 与 Repository 是业务接口,示例省略 imports 和 ViewModel Factory。

**代码练习二 状态与生命周期:**这是结构示例,需要已有 AndroidX Lifecycle、协程依赖以及业务模型。Repository.loadDevices 必须具备主线程可安全调用的挂起实现,阻塞 I/O 由其内部调度。

kotlin
sealed interface DeviceUiState {
    data object Loading : DeviceUiState
    data class Content(val items: List<Device>) : DeviceUiState
    data class Error(val message: String) : DeviceUiState
}
class DeviceViewModel(private val repo: DeviceRepository) : ViewModel() {
    private val _state = MutableStateFlow<DeviceUiState>(DeviceUiState.Loading)
    val state: StateFlow<DeviceUiState> = _state.asStateFlow()
    private var refreshJob: Job? = null
    init { refresh() }
    fun refresh() {
        if (refreshJob?.isActive == true) return
        refreshJob = viewModelScope.launch {
            _state.value = DeviceUiState.Loading
            try {
                _state.value = DeviceUiState.Content(repo.loadDevices())
            } catch (e: CancellationException) {
                throw e
            } catch (e: Exception) {
                _state.value = DeviceUiState.Error("加载失败 请重试")
            }
        }
    }
}
// Fragment.onViewCreated 内 viewModel 已通过合适的 Factory 获取
viewLifecycleOwner.lifecycleScope.launch {
    viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.state.collect { state -> render(state) }
    }
}

延伸追问 ​

停止收集后后台任务一定停止吗?不一定。这里暂停的是 UI 收集,viewModelScope 中已启动的刷新有自己的生命周期;是否取消取决于业务需求。退出并清理 ViewModel 时,其作用域会被取消。(生命周期感知协程)

Jetpack 常用组件的职责补充 ​

组件解决什么问题使用边界
Lifecycle感知拥有者的生命周期状态监听和收集应绑定正确拥有者
Room为 SQLite 提供结构化访问支持持久化设备记录 注意迁移 事务与线程
WorkManager调度可延后且需要可靠执行的工作适合日志补传 不保证某秒立即运行 也不是长连接容器
DataStore保存配置或小型结构化数据不代替适合关系查询的大型数据库
Navigation管理页面导航与相关状态理解导航图与 ViewModel 作用域
Paging分页加载与展示大量数据不应一次把海量设备历史装进内存

Room 的事务保证一组数据库操作整体一致,不会自动覆盖外部设备动作。WorkManager 的“可靠执行”也不等于业务天然恰好一次,重试任务仍然需要幂等设计。选组件先看任务性质,再看框架能力。

内容来源 ​

用完整回答理解机制,用真实证据解释项目。