这个问题问得非常专业 ,而且你问的点已经不是"会不会用 Compose",而是Compose 编译器和运行时是怎么工作的 这一层了 👍
我会按 "你脑子里应该形成的模型" 来讲,而不是背概念。
一句话先给结论(先记住)
@Stable不是告诉 Compose「这个类不会变」,
而是告诉 Compose「这个类的变化是可预测的」
这是 90% 的人理解错的地方。
一、Compose 到底在怕什么?
Compose 最怕的是:
❌ 无法判断一个对象"值有没有变"
因为:
- Compose 靠状态变化触发重组
- 如果它不知道一个参数"有没有变"
- 就只能 保守地重组
二、Compose 判断「是否变化」的规则(非常重要)
默认情况下,Compose 对参数有三种认知:
| 类型 | Compose 如何判断 |
|---|---|
| 基本类型 / String | 直接 == |
@Immutable |
值完全不可变 |
| 普通 class | ❌ 不知道,默认当作会变 |
👉 普通 class = 不可信
三、那 @Stable 到底是什么意思?
官方定义(翻译成人话):
@Stable表示:1️⃣ 对象的
equals()是稳定的2️⃣ 公共属性的变化是可追踪的
3️⃣ 如果属性没变,对象行为不会变
⚠️ 注意:
不是说它不会变,而是"变得可追踪"
四、Compose 知道标了 @Stable 后,会干什么?
这是最关心的点 👇
Compose 编译器 + Runtime 会做三件关键的事
✅ 1️⃣ 参数比较策略改变(核心)
kotlin
@Composable
fun UserCard(user: User) { ... }
❌ 没有 @Stable
text
User 是普通类 → 认为可能变化
→ 父重组时,UserCard 一定重组
✅ 有 @Stable
text
Compose 只在"User 的可观察属性变了"时才重组
📌 直接减少重组次数
✅ 2️⃣ 跳过(Skip)Composable 调用
Compose 有一个非常重要的优化:skip
kotlin
if (参数没变) {
跳过这个 Composable
}
@Stable是 允许 skip 的前提条件
✅ 3️⃣ 推断 State 依赖关系
当 @Stable 类中:
kotlin
@Stable
class UiState(
val title: String,
val count: Int
)
Compose 能推断:
- title / count 没变
- UI 输出一定没变
- 👉 不需要重组
五、一个非常直观的对比例子(一看就懂)
❌ 不加 @Stable(性能差)
kotlin
class CounterState(var count: Int)
@Composable
fun Counter(state: CounterState) {
Text("count=${state.count}")
}
即使 count 没变,只要父重组:
👉 Counter() 必定重组
✅ 加 @Stable(性能好)
kotlin
@Stable
class CounterState(var count: Int)
@Composable
fun Counter(state: CounterState) {
Text("count=${state.count}")
}
现在:
count没变 → 不重组count变了 → 精确重组
六、那和 @Immutable 有什么区别?
| 注解 | 含义 |
|---|---|
@Immutable |
永远不会变(val + 不可变) |
@Stable |
可以变,但变化可追踪 |
用错后果:
- 把可变对象标成
@Immutable→ ❌ BUG - 把随意变的对象标成
@Stable→ ❌ UI 不更新
七、为什么 MVI + Compose 项目里经常看到 @Stable?
因为:
kotlin
data class UiState(...)
或者:
kotlin
class UiState(
val list: List<Item>,
val loading: Boolean
)
- 这些 经常作为 Composable 参数
- 又 频繁参与重组判断
- 标
@Stable= 减少整树重组
👉 在复杂页面上,差距非常明显
八、Compose 并不会"自动帮你标"
Compose 宁可慢,也不冒险:
"我不知道你这个对象会不会偷偷变"
所以你需要 显式告诉它
九、什么时候该用 @Stable?(工程规则)
✅ 适合用的场景
- UI State 类
- ViewModel 暴露给 UI 的对象
- 长生命周期、频繁传递的参数
- 内部字段受控(只在 VM 改)
❌ 不该用的场景
- mutableList / mutableMap 暴露在外
- 随意 set 的 public var
- 非 UI 层对象
- 不确定变化来源的类
十、可以用一句话向别人解释 @Stable(面试级)
@Stable告诉 Compose:
这个对象的变化是可预测的,
如果它的可观察属性没变,UI 就不会变。
最后一句实话(非常重要)
@Stable是性能承诺 ,不是语法糖。
你对它负责,Compose 信你。