mvi架构2-UiEffect作用

mvi架构2-UiEffect作用 mvi架构-UiEffect作用UiState 描述的是 “界面应该呈现什么样子”数据UiEffect 描述的是 “界面应该执行什么动作”行为Intent 描述的是 “用户想做什么”意图Intent 是“输入”UiState 是“长相”UiEffect 是“行为”。为什么不能把 UiEffect 放入 Intent 处理Intent 是“输入”而 UiEffect 是“输出”。Intent 是用户点了一下按钮输入给 ViewModel。处理逻辑 在 ViewModel 内部完成。问题来了ViewModel 处理完后怎么告诉 Compose 界面执行一个不需要改变长相的操作比如弹个 Toast 或跳个页如果 ViewModel 直接在 handleIntent 里弹 Toast它就必须持有 Context 或 Activity。这违反了架构原则会导致内存泄漏且无法测试。所以ViewModel 需要一个出口把这个“动作指令”发出去让 UI 层去接。这个出口就是 UiEffect。为什么不能把 UiEffect 放入 UiState这是新手最容易踩的坑。假设你把“弹窗”放在 UiState 里data class MyUiState(val showToast: Boolean false)灾难场景用户点击按钮ViewModel 设置 showToast true。Compose 观察到 UiState 变了弹出 Toast。关键点此时用户旋转了手机。Compose 重新执行Recomposition读取最新的 UiState。发现 showToast 仍然是 true结果手机又弹了一次 Toast。用户会觉得这 App 有 Bug。结论UiState 是有记忆的Stateful它描述的是“状态”。而弹窗、跳转、播放音乐是瞬时的Stateless发完就得丢掉。UiState vs UiEffect 的本质区别特性UiState (状态)UiEffect (副作用/事件)比喻你的身高、体重一直存在你打了个喷嚏发生就没了存活时间只要页面在数据就在发射出去UI 消费完就消失屏幕旋转自动恢复数据还在屏幕上不会重复触发喷嚏打完不会再打典型例子列表内容、加载中图标、文本框文字弹 Toast、导航跳转、播放音频想象一个“播放音乐”的例子Intent: 用户点击“播放”按钮。ViewModel: 处理业务获取歌曲链接。UiState: 更新 isPlaying true。这时 Compose 看到状态变了把“播放图标”换成“暂停图标”。长相变了UiEffect: 发送 PlayMusic(url)。Compose 里的 LaunchedEffect 收到这个指令调用播放器开始出声。动作执行了如果没有 UiEffect你只能把 url 塞进 UiState。由于 UiState 会被反复读取每当页面因为别的原因比如进度条走动刷新时播放器可能都会被错误地重启导致音乐卡顿或重播。总结UiState 是给 Compose “看”的。UiEffect 是给 Compose “做”的。Intent 是给 ViewModel “听”的。