0

【扔物线】Jetpack Compose:从上手到进阶再到高手

有客999
18天前 8

获课:999it.top/27139/

# 真正理解“状态驱动UI”的人,才能成为Compose高手

## 从一道面试题说起

上周帮团队面试,我问了个看似简单的问题:

“用Compose写一个计数器,点击按钮数字加1。不用IDE,说说思路。”

来面试的是一位有两年Android经验的开发者。他脱口而出:

“定义一个`var count = 0`,按钮点击时`count++`,然后`Text(count.toString())`显示出来。”

我追问:“那界面会更新吗?”

他犹豫了:“会吧……我点了按钮啊。”

**这是Compose初学者最容易踩的坑,也是区分“会用”和“真懂”的分水岭。**

---

## 一、UI不是“画”出来的,是“算”出来的

传统XML开发里,UI是一幅静态的画。

我们要更新某个数字,得先找到那个TextView,然后调用`setText()`。这个过程像什么呢?**你指着画布上的一处说:这里,改成新数字。**

这很符合直觉——我直接操作UI,它就应该变。

**但Compose不这么想。**

在Compose的世界里,UI不是一幅被你反复修改的画,而是**一个函数根据当前状态计算出的结果**。

用公式表达就是:

```

UI = f(state)

```

这个`f`是你的Composable函数,`state`是数据状态。状态变了,函数重新执行,UI重新“算”一遍。

**你不去改UI,你只改数据。** 这个思维的转变,比学会任何Compose API都重要。

---

## 二、为什么新手总觉得Compose“不听话”

回到开头的计数器。

那位面试者的代码,在Compose里**界面不会更新**。为什么?

因为他定义了一个普通的Kotlin变量。Compose在执行函数时读取了这个变量的值(0),把它显示出来。但当变量变成1时,Compose**并不知道这个变化**。

它没被通知,就不会重新执行函数。

这就好比你告诉厨师:“客人点了鱼香肉丝。”厨师做好端上去。过了一会儿客人说:“我改宫保鸡丁了。”但你没告诉厨师,厨师还在厨房刷手机。

**不是Compose不听话,是它根本不知道需要动。**

那怎么让Compose知道呢?

```kotlin

var count by remember { mutableStateOf(0) }

```

`mutableStateOf`包了一层“可观察性”。当`count`变化时,它会主动通知所有用到它的Composable:“我变了,你们重新算一下。”

这就是状态驱动的核心机制:**状态可观察,UI自动响应。**

---

## 三、状态的“三六九等”,你分清楚了吗

很多人在Compose里用状态,全靠一种方式:`remember`。

但高手会用三种:

### 1. remember:单界面、不跨重组

适合临时UI状态,比如展开/折叠、当前输入框文本。界面重建就重置。

### 2. rememberSaveable:保命状态

适合应对屏幕旋转、进程回收。自动存入Bundle,绕过了remember的“健忘症”。

### 3. ViewModel:跨界面、跨生命周期

适合业务数据。即使界面关了又开,数据还在。搭配`collectAsState()`使用。

有个简单的选择题:

- 这个状态**只有这个界面关心**,且**重建不要紧** → `remember`

- 这个状态**只有这个界面关心**,但**重建不能丢** → `rememberSaveable`

- 这个状态**多个界面关心**,或**必须存到数据库/网络** → ViewModel

选对了,状态不会乱窜;选错了,bug悄悄埋下。

---

## 四、状态提升:把“糊涂”组件变“通透”

我在项目里见过这样的代码:

一个自定义的`ProfileCard`,内部自己维护着用户资料的状态,自己发起网络请求,自己显示加载中、成功、失败。

看起来很方便,对不对?

问题来了:**如果另一个页面也想复用这个卡片,但数据要从别的地方传过来呢?**

这个卡片“太自主”了——它不听外界的,只信自己。

解决方案是**状态提升(State Hoisting)**。把状态从组件内部“拎”出来,交给上层管理:

```kotlin

// 不推荐

@Composable

fun ProfileCard(userId: String) {

    var user by remember { mutableStateOf(loadUser(userId)) }

    // 显示用户信息

}

// 推荐

@Composable

fun ProfileCard(

    user: User,  // 状态从外部传入

    onUserUpdate: (User) -> Unit  // 事件回调

) {

    // 只管显示

}

```

这背后是一条黄金法则:**谁需要修改状态,谁就应该持有状态。**

底层组件只负责“呈现”,不负责“决定”。这样的组件可预测、易测试、好复用。

---

## 五、重组不是洪水猛兽,滥用才是

很多人听说“Composable函数会频繁执行”,吓得把大量计算塞进`LaunchedEffect`或`derivedStateOf`。

其实**重组是正常的,慢的重组才是问题。**

Compose有一套智能跳过机制:如果一个参数没变,对应的UI部分就不会重新执行。

所以优化的核心不是“少重组”,而是**让重组发生在真正需要的时候**,并且**不要在重组时做昂贵操作**。

几个实用判断:

- 从ViewModel拿数据?用`collectAsState()`已经优化好了

- 列表项里做复杂计算?移到外面用`remember`缓存结果

- UI临时状态和业务状态混在一起?拆分它们

**大多数场景下,Compose的重组比你想象中快。先写出清晰的逻辑,再考虑优化。**

---

## 六、成为高手的那个瞬间

我曾经带过一位同事,他学Compose三个月,能写、能改、能上线。但总觉得哪里别扭。

有一天他在改一个订单详情页,页面有七八个状态:订单信息、物流轨迹、倒计时、售后入口……代码里散落着七八个`mutableStateOf`,逻辑交织,改一个怕动三个。

我问他:“你觉得这个页面本质上在描述什么?”

他想了很久:“一个订单从生成到完成的不同样子。”

“对。所以你不需要管理七个状态,你只需要管理一个——**订单状态**。其他UI分支,都是根据它计算出来的。”

他重构了代码,删掉五个状态变量,只保留了`orderState`,UI逻辑瞬间清晰。

那天他跟我说:“我终于知道‘状态驱动UI’是什么意思了——**我不指挥UI做什么,我只告诉它,数据现在是哪一页。**”

那一刻,我知道他成了真正的高手。

---

## 写在最后

Compose不难学,一周就能上手写页面。但要把代码写得清晰、稳定、易维护,需要的是对“状态”的深刻理解。

状态是数据,UI是投影。  

你不操纵投影,你调整光源。  

光源变了,投影自然随之而动。

**初学者关心“界面长什么样”,高手关心“状态是什么结构”。**  

**初学者在写界面,高手在设计状态的演化路径。**

如果你现在回头看看自己写的Compose代码,  

那个让UI更新的“源头”,  

是一个清晰定义、单一来源、可预测变化的状态模型,  

还是散落在各个Composable里的零星变量?

想清楚这个问题,你就离真正的Compose高手,不远了。

---

**互动:**

你在Compose状态管理上踩过最大的坑是什么?  

评论区聊聊,点赞最高的三位,送你一份我整理的《Compose状态管理实战案例集》。



本站不存储任何实质资源,该帖为网盘用户发布的网盘链接介绍帖,本文内所有链接指向的云盘网盘资源,其版权归版权方所有!其实际管理权为帖子发布者所有,本站无法操作相关资源。如您认为本站任何介绍帖侵犯了您的合法版权,请发送邮件 [email protected] 进行投诉,我们将在确认本文链接指向的资源存在侵权后,立即删除相关介绍帖子!
最新回复 (0)

    暂无评论

请先登录后发表评论!

返回
请先登录后发表评论!