引言

在 Jetpack Compose 重组中可能会包含逻辑,要是逻辑简单,可能没啥影响;可要是逻辑计算成本高,不知不觉就会拖慢 App 性能。 本篇文章就来讲讲如何用 derivedStateOf 帮你优化这类情况。你会看到它如何自动缓存派生逻辑的结果,只有真正依赖项变了,才会重新计算。

问题:举个代码例子理解

假设你要验证邮箱逻辑,在 TextField 输入内容时触发验证:

val isValid = email.endsWith("@gmail.com")

然后在 Composable 函数里调用,像这样:

@Composable
fun EmailValidator() {
    var email by remember { mutableStateOf("") }
    val isValid = email.endsWith("@gmail.com")
}

每次输入时(比如输入 richaricha.sharmaricha.sharma@gmail ),都会触发重组,endsWith 方法也会每次重新执行。这逻辑看着小,可要是换成复杂正则或解析逻辑呢?因为这些表达式就在 @Composable 函数里,Compose 不会记住之前结果,每次都得重新算整个代码块。

问题就在于“没必要的重复计算”。不是每次重组都会改变计算的输入,但 Compose 还是会执行,原因是:

  • 它会重新调用 @Composable 函数。
  • 会重新计算所有表达式。

解决方案:用 derivedStateOf

修改代码,用 derivedStateOf

val isValid by remember {
    derivedStateOf {
        email.endsWith("@gmail.com")
    }
}

Compose 会跟踪 email 。只有 email 变了,才会重新计算 isValid 。要是因为其他原因(比如主题变化、无关状态改变)导致 UI 重组,验证逻辑也不会重新计算。

深入理解 Compose 内部原理

我们在 remember { ... } 里用 derivedStateOfremember 能在重组时保留代码块的结果。

derivedStateOf 的函数签名是这样:

fun <T> derivedStateOf(calculation: () -> T): State<T>

derivedStateOf 会创建一个特殊的 State 对象(技术上是 DerivedState<T> ),它的作用:

  • 包含计算逻辑(这里就是 email.endsWith(...) )。
  • 跟踪输入依赖。

remember 能保证未来重组时,同一个 DerivedSnapshotState 被复用,不会重新创建,也不会丢失缓存值。

DerivedSnapshotState 是个特殊的内部类,功能如下:

  • 实现 State<T> ,所以有 .value 属性。
  • 缓存最后一次计算的结果。
  • 跟踪 lambda 里读取了哪些 state 对象。
  • 知道啥时候该让缓存失效。

内部工作流程拆解

val isValid = derivedState.value

上面代码中 DerivedSnapshotStatevalue 获取逻辑大概这样(伪代码示意):

override val value: T
    get() {
        if (currentValue is valid) {
            return currentValue
        } else {
            // 执行计算逻辑(比如 email.endsWith(...) )
            // 跟踪 lambda 里读了哪些 State 对象
            // 把结果存起来,下次用
            return newValue
        }
    }

要是缓存值有效,直接返回,不重复计算。 要是缓存失效:

  1. 执行 lambda,比如 email.endsWith("@gmail.com")
  2. 记录读取了 email 这个状态。
  3. 存储计算结果。
  4. 返回新结果。

email 变化时,Compose 知道 lambda 里读了 email ,就会把派生状态标记为“失效”。下次读 .value 时,就会用新的 email 重新执行 lambda。

要是 email 没变化,却因为其他原因触发重组,就会复用缓存值,不用重新计算——效率更高!

总结:derivedStateOf 的意义

derivedStateOf 是 Compose 实现“记忆化计算”的方式,只有依赖的状态变了,才会重新计算,不是每次重组都算。

它是你控制 Compposable 里“昂贵/没必要的重复计算”的工具。不会阻止重组,但能让派生值 只在真正依赖项变化时 重新计算,而非 UI 每次有小更新都算。

下次写验证、筛选、复杂正则,或者其他派生值逻辑时,停一停,问问自己:

  • 这逻辑需要每次重组都执行吗?
  • 能不能用 derivedStateOf 包一下,提升效率?

只需加一点代码,就能让 Compose UI 更快、更高效、更可预测——这正是现代 Android 应用需要的!

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐