单向数据流 (UDF)
单向数据流原则 (Unidirectional Data Flow)
在 Android 开发中,单向数据流是构建可预测、可维护 UI 的核心原则。
在 Jetpack Compose 中的实现
// ✅ 正确:单向数据流dataclassPlaylistUiState(valplaylists:List<Playlist>=emptyList(),valisLoading:Boolean=false,valerror:String?=null)classPlaylistViewModel:ViewModel(){privateval_uiState=MutableStateFlow(PlaylistUiState())valuiState:StateFlow<PlaylistUiState>=_uiState.asStateFlow()// 唯一的状态变更入口funonEvent(event:PlaylistEvent){when(event){isPlaylistEvent.LoadPlaylists->loadPlaylists()isPlaylistEvent.DeletePlaylist->deletePlaylist(event.id)isPlaylistEvent.SelectPlaylist->selectPlaylist(event.id)}}privatefunloadPlaylists(){viewModelScope.launch{_uiState.update{it.copy(isLoading=true)}try{valplaylists=playlistRepository.getAll()_uiState.update{it.copy(isLoading=false,playlists=playlists)}}catch(e:Exception){_uiState.update{it.copy(isLoading=false,error=e.message)}}}}}// UI 层:纯渲染,不含业务逻辑@ComposablefunPlaylistScreen(viewModel:PlaylistViewModel=viewModel()){valuiStatebyviewModel.uiState.collectAsState()PlaylistContent(state=uiState,onLoadClick={viewModel.onEvent(PlaylistEvent.LoadPlaylists)},onDeleteClick={id->viewModel.onEvent(PlaylistEvent.DeletePlaylist(id))})}❌ 常见的反模式
// ❌ 错误:直接在 UI 层修改状态@ComposablefunBadExample(){varcountbyremember{mutableIntStateOf(0)}Button(onClick={count++}){// 直接修改状态Text("点击:$count")}}// ❌ 错误:ViewModel 直接操作 UIclassBadViewModel:ViewModel(){funupdateUI(){// ❌ 绝对不要持有 Compose 的引用// activity?.runOnUiThread { ... }}}与双向绑定的对比
| 特性 | 双向绑定 (传统) | 单向数据流 (Compose) |
|---|---|---|
| 数据来源 | View 和 ViewModel 都可以修改 | 只有 ViewModel |
| 调试难度 | 难(状态可能在多处被修改) | 易(变更追踪唯一) |
| 测试性 | 需要 mock UI 组件 | 可独立测试 ViewModel |
| 复杂度 | 简单场景简单 | 需要定义 Action/State |
简单来说:UI 只负责"展示",不负责"判断"和"修改"。所有决策逻辑都集中在 ViewModel 层。
