compose中的@Stable概念和用法

这个问题问得非常专业 ,而且你问的点已经不是"会不会用 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 信你。

相关推荐
至乐活着3 天前
Docker Compose多服务编排实战:从零搭建Node.js+MySQL+Redis全栈应用
docker·微服务·devops·容器编排·compose
le16161614 天前
Android Compose——尺寸修饰符的调用顺序构成的不同尺寸约束效果
android·compose·modifier
le16161615 天前
Android Compose Modifier修饰符
android·compose·modifier
小书房15 天前
Android UI为什么由XML转向Compose
xml·ui·compose·声明式ui
le16161616 天前
Android Compose基础布局——从传统XML的视角切入了解
xml·compose
赏金术士21 天前
企业级 Jetpack Compose 项目(入门版)最佳结构
android·kotlin·compose
Jomurphys22 天前
Compose 调用 - 液态玻璃 Backdrop
android·compose
氦客25 天前
Android Compose 图层的合成 : BlendMode
android·compose·jetpack·layer·blendmode·graphics·图层的合成
赏金术士1 个月前
第六章:UI组件与Material3主题
android·ui·kotlin·compose
赏金术士1 个月前
Jetpack Compose 底部导航实战教程(完整版)
android·kotlin·compose