外观
怎样写一个能解释清楚的 MVVM 示例?
标准回答
状态只从 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 的“可靠执行”也不等于业务天然恰好一次,重试任务仍然需要幂等设计。选组件先看任务性质,再看框架能力。
内容来源
- Android 智能硬件 SDK 技术 Wiki · 原条目
Q32