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 的用法。

GitHub 加速计划 / compose / compose
78
5
下载
compose - Docker Compose是一个用于定义和运行多容器Docker应用程序的工具,通过Compose文件格式简化应用部署过程。
最近提交(Master分支:6 个月前 )
036da479 Signed-off-by: Sebastiaan van Stijn <github@gone.nl> 29 天前
33172d5e Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com> 29 天前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐