Compose中的一些机制验证与总结——remember

最近在搞一个基于 Compose 实现的低代码跨平台项目,涉及到一些 Compose 运行时的一些机制问题,周末写了个 demo 验证总结一下,总体是与过往经验相符的,也发现了一些小的细节是以前不太清楚的,可以一起学习研究一下,如有错误欢迎指正!

remember 简介

先贴一段 GPT4 给的简介:

remember 是 Jetpack Compose 中的一个核心函数,它用于记住那些你不希望在重组(recomposition)时重新创建的数据。举个例子,这可能是一种状态、一个对象实例或一个计算成本较高的结果。它有助于保持性能并避免不必要的计算。

使用示例

先看测试代码:

kotlin 复制代码
@Composable
fun ContentView(index: Int) {
    val item = remember {
        mutableStateOf(DataA()).also {
            Log.d("Test", "ContentView in remember, item=$it")
        }
    }
    
    Column(
        modifier = Modifier
            .fillMaxSize()
            .border(3.dp, Color.Cyan)
    ) {
        Text(text = "this is page $index,\n item= $item")
    }
}


class DataA(
    val id: Int = 0,
    val name: String = ""
)

上面的代码中,我们定义了一个 ContentView 内容页,外面传进来一个 index,在 ContentView 组件内,我们用 remember 来包一个state变量,remember 里面打印一行日志方便观察 remember 内部的执行情况,页面就只显示"this is page index,\\n item= item",打印出当前页面的 index 和 remember 的 item 对象,非常简单

接下来看我们怎么使用它:

常规使用

kotlin 复制代码
@Composable
fun TestCompose2() {
    var index by remember {
        mutableIntStateOf(0)
    }
    Column {
        Row {
            repeat(3) { repeatIndex ->
                TabBtn(repeatIndex) { tabIndex ->
                    index = tabIndex
                    Log.v("Test", "onClicked: tab_$tabIndex")
                }
            }
        }
        ContentView(index = index)
    }
}

@Composable
fun TabBtn(index: Int, onClicked: (Int) -> Unit) {
    OutlinedButton(onClick = { onClicked(index) }) {
        Text(text = "Tab $index")
    }
}

写过 compose 的同学应该脑海中已经有画面了对吧,没错,跑起来是这样的:

查看日志,remember 里面的日志也打印了

然后点击上面的 tab1,使页面切换:

我们发现页面切换之后,page 更新为 0 了,但是 item 对象还是没有变,而且remember 里面的那行日志也不会再次打印。

因为其实我们这样并不是真正的切换了 ContentView,ContentView 还是那个 ContentView,只是我们给他传的参数变了,使它发生重组(界面刷新)而已,而 remember 的作用正是处理这种重组的情况的

对比不使用remember的情况

而如果我们不使用 remeber ,将 ContentView 里的 item 那段改为如下:

kotlin 复制代码
	val item = run {
        mutableStateOf(DataA()).also {
            Log.d("Test", "ContentView in run, item=$it")
        }
    }

那么每次点击 tab 切换的时候,ContentView 发生重组,都会执行一遍这里的逻辑

所以这里每次都不一样了,即使切回到 tab0 之后,也不再是之前那个 item,所以这就是使用和不使用 remember 的区别

remember 无法处理的情况

如果我们稍微改一下用 ContentView 的方式,如下末尾 3 行代码:

kotlin 复制代码
@Composable
fun TestCompose2() {
    var index by remember {
        mutableIntStateOf(0)
    }
    Column {
        Row {
            repeat(3) { repeatIndex ->
                TabBtn(repeatIndex) { tabIndex ->
                    index = tabIndex
                    Log.v("Test", "onClicked: tab_$tabIndex")
                }
            }
        }
        when (index) {
            0 -> ContentView(index = 0)
            1 -> ContentView(index = 1)
            else -> ContentView(index = 2)
        }
    }
}

此时即使 ContentView 里用的时 remember,每次打印出的 item 对象也已经是新的对象了。如下:

这是因为此时切换 tab 之后实现了真正的切换不同的 ContentView,旧的 ContentView 被移出了可组合范围,下一次再回来的时候就会重新执行一遍 remember 内的逻辑。换句话说,remember 只能在函数存在于组合树且未被移除时保持其状态。

总结:

  1. remember 用于保存数据,这些数据只应该在 Compose 函数的重组过程中保持不变,但不跨过函数的移除和添加
  2. 使用 remember 保存的对象当 Compose 函数被移出组合树后不会保留。
  3. 当 Compose 函数再次被组合进入时,remember 将重新获得一个新的对象。
  4. 要跨组合保持对象,应使用外部状态管理,如 ViewModel。
相关推荐
三少爷的鞋10 小时前
AI 时代,我们都将成为通才型开发者:只懂 Android,已经不够了
android
Dovis(誓平步青云)14 小时前
家里设备越来越多,如何用一张空间地图控制灯光和温度![
android·java·前端·javascript·人工智能·电脑
晚风叙码16 小时前
MySQL 数据类型详解:从数值到字符串,一篇讲透
android·mysql·adb
传奇开心果编程16 小时前
【Compose Multiplatform 跨端开发学与练】第3课 布局与组件
android·windows·学习·ui·ios·kotlin·composer
传奇开心果编程19 小时前
【Compose Multiplatform 跨端开发学与练】第8课 资源管理与主题
android·windows·学习·ios·kotlin·web·composer
传奇开心果编程20 小时前
【Compose Multiplatform 跨端开发学与练】第9课 测试与调试
android·学习·macos·ios·kotlin·web·composer
传奇开心果编程21 小时前
【Compose Multiplatform 跨端开发学与练】第4课 导航与路由
android·windows·学习·ui·ios·kotlin·composer
传奇开心果编程21 小时前
【Compose Multiplatform 跨端开发学与练】第6课 状态管理与架构
android·学习·ui·ios·架构·kotlin·composer
事圆则缓21 小时前
Android AOSP 定制常见概念:源码目录、系统镜像与刷机流程
android
传奇开心果编程21 小时前
【Compose Multiplatform 跨端开发学与练】第2课 Compose 基础语法
android·windows·学习·ui·ios·kotlin·composer