一、UI 架构的定义
UI 系统的作用是把“用户操作和数据变化”映射为“界面变化”。
从输入(Action+data)到输出(UI)包含了诸多工作,比如有:
- 事件分发与意图解析
- 数据获取与聚合
- 业务逻辑处理
- 界面渲染
...等等
我们进行架构设计就是去:
- 确定设立哪些功能模块;
- 确定每一项工作交给哪个模块负责;
- 确定各个模块之间的依赖关系和通信方式;
用一句话总结
UI架构就是UI系统中各模块职责划分、依赖关系以及数据流转方式的整体设计。
UI 架构解决的问题是—如何把“用户意图(操作)和数据变化”高效、稳定地映射为“界面变化”。
UI 架构的演化本质上是为了应对不断增长的复杂度。
当“数据变化”越来越频繁、“用户交互”越来越复杂时,采用原有架构:
- 开发成本和难度不断上升;
- 系统运行时候的稳定性和效率不断下降;
于是架构不断演进——
二、UI 架构演化五阶段
🥇 第一阶段:无架构 / 混沌阶段
🧠 核心思想
对每个UI 组件,逐个编码操作。
整个业务流程+UI操作在代码层是混合在一起的,具体每个业务需要操作哪些UI这些都是程序员的脑子在记忆。
textview = findView(R.id.tv)
textView.setText("Hello");
✅ 优势
- 简单直接
- 易于上手
- 微小项目开发效率高
❌ 问题
数据和界面强耦合:当一个数据变化,关联页面很多,要手动更新五六个按钮,极其容易漏掉,导致“显示不一致。
多人协作容易混乱
业务逻辑不可复用
🚨 演化契机
项目变大,代码乱到无法维护时候,开发者开始意识到—UI 和逻辑不能混作一团,于是引入分层思想。
🥈 第二阶段:分层架构(MVC / MVP)
🧠 核心思想
UI 和逻辑要分离
- Model:数据
- View:界面
- Presenter / Controller:逻辑
✅ 优势
- 解耦 UI 和业务逻辑
- 提升可维护性
- 更适合团队开发
❌ 问题
- 接口爆炸(IView、IPresenter)
- 各个模块相互引用,内存泄漏风险高
- 双向调用,数据流混乱
🚨 演化契机
开发者逐渐意识到:
问题不在“分层”,而在决定UI的因素没有被显式建模和统一管理,
例如
- 加载中?还是加载失败?还是加载成功?
- 用户点过按钮了吗?
- 当前展示的是旧数据还是新数据?
- 多个请求回来,哪个是“最终结果”
...等等
这些控制UI的因素是隐式地分散在 UI 容器、业务逻辑、数据层、异步流程以及用户交互等多个维度中,系统缺乏统一的ui更新因素输入源,从而引发复杂性和大量不可预测问题。
这就好比:一个基层执行人员每天的工作是由公司几十个领导指派,那就很容易出现各种问题,比如任务前后矛盾,任务过载堵塞等。
🥉 第三阶段:状态驱动(MVVM)
🧠 核心思想
- 引入状态概念:状态 = 在某一时刻,完整决定 UI 长什么样的最小信息集合。
- 状态被显性建模,并指出:UI = f(State)
public class MyViewModel(
// 状态定义Start
public MutableLiveData<Boolean> isLoadingLiveData = new MutableLiveData<>();
public MutableLiveData<List<Item>> showListLivaData = new MutableLiveData<>();
public MutableLiveData<String> errorLiveData = new MutableLiveData<>();
// 状态定义End
)
从此UI 的显示逻辑由 state 统一控制推导,而不是分散在各处手动操作。
✅ 优势
- 状态被显式建模
- UI 变成“可推导”的
- 更容易测试和维护
❌ 问题
- 状态可能出现非法组合:因为一个页面状态可以是“碎片化”的,它们之间没有建立“关系约束”;
- 状态变化没有约束,会出现从状态A变化到一个错误状态C的问题;
- UI 更新仍然是手动同步:手工写如何根据状态怎么更新UI的操作。
🚨 演化契机
开发者进一步思考:
当状态变量多了,无统一管理,容易出现非法状态——状态整合
状态量碎片化,导致状态变化难以约束,状态转换频频出错——状态切换约束(状态机)
🏅 第四阶段:单向数据流(MVI / Redux)
🧠 核心思想
状态整合
状态变化必须可追踪、可推导
Stateₙ₊₁ = Reduce(Stateₙ, Action)
👉 本质是一个:状态机
这一阶段的本质升级:
从:
“状态存在”
变成:
状态成为一个原子整体
状态如何变化也被建模
把“状态变化过程”彻底规范化
✅ 优势
- 状态变化可预测
- 易于调试(可回放)
- 消灭“非法状态组合”
❌ 问题
- UI 仍然需要手动更新
- 模板代码较多
- 开发学习难度高
🚨 演化契机
开发者发现:
状态管理已经很不错,但 是从状态到UI的映射仍然是“手动同步”的
🧪 第五阶段:声明式 UI(React / Compose)
代表:Jetpack Compose
🧠 核心思想
UI = f(state)
之前阶段 :UI = “如何从 A 变到 B”,在这个阶段变为“当前应该是什么样”
- 之前阶段:关注“过程”
- 第5阶段:关注“结果”
❌ 传统 UI 的本质
UI 是“可变对象”
你在做不断修改它
✅ 声明式UI的本质
UI 是“计算结果”
你在做:每次重新算一个新的 UI
声明式 UI 不是“控制 UI 的变化”而是“不断给出 UI 的完整定义”,未被声明的部分不会存在。
示例
@Composable
fun ListScreen(state: UiState) {
when (state) {
is Loading -> LoadingView()
is Success -> ListView(state.data)
is Error -> ErrorView()
}
}
第一次状态更新:Loading 状态
when (state) {
is Loading -> LoadingView()
}
👉 UI 树:
Root
└── LoadingView
状态变成 Success
when (state) {
is Success -> ListView()
}
👉 新 UI 树:
Root
└── ListView
🔥 关键发生了什么?
Compose 在内部做了这件事:
旧树: LoadingView
新树: ListView
→ diff
→ 删除 LoadingView
→ 添加 ListView
❗重点来了
👉 你没有写:
hide(LoadingView)
👉 但系统帮你做了:
从 UI 树中移除
🧠 这就是“声明式”的真正含义
❌ 传统命令式UI
你:把 LoadingView 隐藏
✅ 声明式
你更新State,Loading is false
👉 系统推导出:现在 UI 不应该有 LoadingView,
那就删掉LoadingView
✅ 优势
- UI 自动与状态一致
- 不再需要手动同步
- 消灭 UI 状态错乱
- 更接近函数式编程
❌ 问题
- 状态设计难度上升(核心问题转移)
- 副作用管理复杂(除了更新 UI State 以外,所有会对外界产生影响的操作,都是副作用,比如Toast、读写数据库、修改全局变量、打日志等)
- 学习成本较高
- 容易写出“伪声明式代码”
🚨 演化契机
问题再次升级:
❗UI 已经很好,但状态建模成为瓶颈
三、传统UI架构演化的终极目标
从整个历史来看,UI 架构一直在逼近一个目标:
🎯 终极形态
UI = f(State)
State = g(UserIntent, Data)
🔥 核心特征
- UI 完全由状态决定
- 状态变化可预测
- 无副作用污染(状态更新时候只更新 UI State ,不会有对外界产生影响的其他操作)
- 系统自动完成更新
👉 用一句话总结:
当 UI 完全由状态决定,状态变化完全由可追踪的行为驱动时,系统就从“不可控的黑箱”,变成了一个“可以被理解、被推演、甚至被回放的白箱系统”——这正是 传统UI 架构演化的终点。
但现在AI来了,一切又不一样了
四、AI时代的演化方向
开发者不想再“手写状态机”
MVI / Redux 本质是:
👉 人类在手动维护一个“状态机”
但问题是:
- 状态爆炸(State explosion)
- 分支复杂(if / else 地狱)
- 维护成本极高
👉 本质:
❗ 人在“模拟智能”,但人并不擅长这个
AI(尤其是大模型)带来了一个非常关键的能力:
👉 从“规则驱动”变成“意图驱动”
过去:
用户点击 → Action → reducer → State → UI
现在开始变成:
用户表达 → AI理解(Intent) → 生成状态 / UI → 渲染
👉 关键变化:
“状态不再完全由人定义,而是可以被推导 / 生成”
过去:
Human: 定义 State + Transition
现在:
AI: 推导 State
Human: 定义约束(Constraint)
👉 核心变化不是“AI生成UI”
而是:
人从“构造状态机”,变成“约束状态空间”
如果说过去的 UI 架构是在解决“如何让状态不失控”,那么未来的 UI 架构,将是在解决——当状态本身由机器生成时,人类如何依然掌控系统的边界。