Skip to content
Maozy's Blog
Go back

[Jetpack Compose] remember

Table of contents

Open Table of contents

一、为什么需要remember

在传统 View 系统里,UI 更新是去修改属性(setText)。

但在 Compose 里,UI 更新(重组)意味着这个函数被重新执行了一遍。 --- UI 即函数

既然是重新执行函数,那函数里面的局部变量会发生什么?

答案是:会被全部重新创建、重新初始化。

这就是 remember 必须要出场的两大核心场景:

场景一:保住组件“状态”不被重置(最常见场景)

假设我们现在在写一个微信的点赞按钮

1.1 错误示范(没有 remember)

@Composable
fun LikeButton() {
    // 每次 LikeButton 这个函数被重新执行时,isLiked 都会被重新变成 false!
    val isLiked = mutableStateOf(false) 

    Button(onClick = { isLiked.value = !isLiked.value }) {
        Text(if (isLiked.value) "取消赞" else "点赞")
    }
}

问题现场:

  1. 点击了按钮,onClick 触发,把 isLiked 改成了 true
  2. Compose 收到状态变更通知,立刻重新执行 LikeButton() 函数来刷新 UI。
  3. 结果刚一进函数第一行,isLiked 又被这句代码初始化成了 false,按钮永远都是“点赞”状态。

1.2 正确示范(加上 remember)

@Composable
fun LikeButton() {
    // 加上 remember
    val isLiked = remember { mutableStateOf(false) } 

    Button(onClick = { isLiked.value = !isLiked.value }) { ... }
}

这里 remember 的作用:

  1. 第一次执行函数时,rememberfalse 这个状态装进“保险箱”存起来。
  2. 当点击按钮把状态变成 true 并触发重组(函数第二次执行)时,代码走到这一行时,remember 说:“停!我‘保险箱’里有上次存的值 true,不要再执行大括号里的 mutableStateOf(false) 了,直接把‘保险箱’里的拿去用。”
  3. 于是,点赞状态成功保留了下来。

场景二:避免重复执行“耗时操作”(性能优化场景)

假设界面每次刷新,都要对一个包含 10 万条数据的 List 进行极其复杂的过滤和排序。

2.1 错误示范(没有 remember)

@Composable
fun UserList(users: List<User>) {
    // 极其耗时的操作,比如耗时 200 毫秒
    val sortedUsers = users.filter { it.isActive }.sortedBy { it.score } 
    
    // 假设下面还有动画,导致这个函数每秒钟被重组 60 次
    LazyColumn { ... }
}

问题现场: 如果界面有动画或其他状态变化导致不断重组,这个耗时的排序操作每秒会被执行 60 次!手机直接卡死。

2.2 正确示范(使用 remember 缓存计算结果)

@Composable
fun UserList(users: List<User>) {
    // 只有第一次进这个函数时,才会执行大括号里的耗时排序
    // 后续哪怕疯狂重组,只要 users 这个参数没变,就直接返回上次排好序的列表
    val sortedUsers = remember(users) { 
        users.filter { it.isActive }.sortedBy { it.score } 
    }
    
    LazyColumn { ... }
}

这里留意加了一个参数 remember(users):这是带 Key 的 remember,它的含义是:

二、总结

在 Compose 中,由于状态变更会引发 Composable 函数的频繁重新执行(重组),

如果我们在函数内部直接声明变量或执行耗时计算,每次重组都会导致变量重置或重复计算。

remember 的作用就是在重组的生命周期中跨越函数调用来缓存对象

它主要解决两个问题:

一是包裹 State 以防止状态在重组时丢失;

二是缓存高开销的计算结果,避免在每一帧重组时造成性能浪费。


Share this post on:

Previous Post
[Jetpack Compose] state读取与追踪
Next Post
[Android底层] CPU & GPU