聊聊 Jetpack Compose 的 “状态订阅&自动刷新” -- remember 和重组作用域

Jekpack Compose “状态订阅&自动刷新” 系列:

     【 聊聊 Jetpack Compose 的 “状态订阅&自动刷新” - - MutableState/mutableStateOf 】

     【 聊聊 Jetpack Compose 的 “状态订阅&自动刷新” - - remember 和重组作用域 】

     【 聊聊 Jetpack Compose 的 “状态订阅&自动刷新” - - 有状态、无状态、状态提升?】

     【 聊聊 Jetpack Compose 的 “状态订阅&自动刷新” - - mutableStateListOf 】

     【 聊聊 Jetpack Compose 的 “状态订阅&自动刷新” - - 你真的了解重组吗?】


在讲本篇文章主题之前,建议先看看 聊聊 Jetpack Compose 原理 – 状态订阅&自动刷新机制 一文,因为两篇文章是上下篇的关系,看完上篇,可以更好的串联知识点。

话不多说,还是老样子,从 Demo 一步步引出我们的核心知识点。

一、话题引入

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        var name by mutableStateOf("Compose")

        setContent {
            Text(name)

            LaunchedEffect(true) {
                delay(3000)
                name = "Kotlin"
            }
        }
    }
}

一段很简单的代码示例,就是前一篇文章的开头示例,我们看下效果:

在这里插入图片描述

很简单,3s 后从 Compose 变为 kotlin。

现在我们改下代码:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            var name by mutableStateOf("Compose")
            Text(name)

            LaunchedEffect(true) {
                delay(3000)
                name = "Kotlin"
            }
        }
    }
}

我们把 var name by mutableStateOf("Compose") 从 setContent 外面挪到里面来了。

执行看下效果:

在这里插入图片描述

奇怪了,文字没有刷新!

二、重组作用域

问题出在哪?首先我们回顾下 Compose 是怎么刷新界面的?比如上面的例子,当 name 重新赋值后,读取它的地方会被标记为失效,然后重组刷新。

在 Compose 中并不是单纯的刷新 Text(name) 这一行,而是会把包含 Text(name) 的代码块给包起来,刷新的是整个代码块,或者说重组整个代码块:

在这里插入图片描述

所以这段蓝色背景的代码块会被重新执行一遍,这个蓝色区域就是:重组作用域(Recompose scope)

所以问题原因你应该就能找到了:

不仅仅 Text(name) 会被重新执行,var name by mutableStateOf("Compose") 也会执行!name 又被重新初始化了。

在这里插入图片描述

三、remember

那怎么解决?其实开发工具已经提示你了:

在这里插入图片描述

mutableStateOf 是标红的,并且错误提示:Creating a state object during composition without using remember.

意思就是:你在组合过程里面创建了 StateObject 对象,但是没有用 remember。

所以怎么包?如下:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            var name by remember { mutableStateOf("Compose") }
            Text(name)

            LaunchedEffect(true) {
                delay(3000)
                name = "Kotlin"
            }
        }
    }
}

加了 remember 后,在第一次执行的时候,会执行 Lambda 表达式,也就是执行 mutableStateOf("Compose"),并且 remember 会保存结果(StateObject),再次调用的时候会直接返回保存的老对象(StateObject),而不是在执行 Lambda 表达式里面的代码,相当于充当了缓存的功能。

我们执行看下:

在这里插入图片描述

所以 remember 起到缓存作用,就是为了防止多次初始化变量而导致程序不可控,所以在 Compose 里面你只要 mutableStateOf,那么能加 remember 我们就加 remember。

另外我们需要注意 remember 是可以带参数的:可以一个或者多个参数。

@Composable
inline fun <T> remember(
    key1: Any?,
    calculation: @DisallowComposableCalls () -> T
): T {
    return currentComposer.cache(currentComposer.changed(key1), calculation)
}

@Composable
inline fun <T> remember(
    key1: Any?,
    key2: Any?,
    calculation: @DisallowComposableCalls () -> T
): T {
    return currentComposer.cache(
        currentComposer.changed(key1) or currentComposer.changed(key2),
        calculation
    )
}

有什么用,我们举个例子说明,比如我们自定义了一个 Composable 函数:

@Composable
fun showCharCount(value: String) {
    val length = value.length
    Text("字符串长度:$length")
}

很简单,一个显示字符串长度的函数。

我们假设一场场景:

  1. 传入进来的字符串特别长,
  2. 并且 showCharCount 反复被调用了很多次。

那么 value.length 每次都被调用,就显得有点笨重了,所以我们可以给它加上一个 remember。

@Composable
fun showCharCount(value: String) {
    val length = remember {
        value.length
    }
    Text("字符串长度:$length")
}

但此时就会出现一个问题:

@Composable
fun showCharCount(value: String) {
    // 第一次传进来:abcd
    // 第二次传进来:abcdefg
    // 第三次传进来:abcdefghijklmn
    val length = remember {
        value.length
    }
    Text("字符串长度:$length")
}

字符串长度永远都是:4,因为 value.length 不会被执行,那怎么办:

@Composable
fun showCharCount(value: String) {
    // 第一次传进来:abcd
    // 第二次传进来:abcdefg
    // 第三次传进来:abcdefghijklmn
    val length = remember(value) {
        value.length
    }
    Text("字符串长度:$length")
}

我们给 remember 加了一个参数:value,只要这个 key 变化了,那么就会重新执行 Lambda 表达式,这就是带参数的 remember 的用法。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值