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。
相关推荐
落落落sss1 小时前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
代码敲上天.2 小时前
数据库语句优化
android·数据库·adb
GEEKVIP4 小时前
手机使用技巧:8 个 Android 锁屏移除工具 [解锁 Android]
android·macos·ios·智能手机·电脑·手机·iphone
model20056 小时前
android + tflite 分类APP开发-2
android·分类·tflite
彭于晏6896 小时前
Android广播
android·java·开发语言
与衫7 小时前
掌握嵌套子查询:复杂 SQL 中 * 列的准确表列关系
android·javascript·sql
500了14 小时前
Kotlin基本知识
android·开发语言·kotlin
人工智能的苟富贵14 小时前
Android Debug Bridge(ADB)完全指南
android·adb
小雨cc5566ru19 小时前
uniapp+Android面向网络学习的时间管理工具软件 微信小程序
android·微信小程序·uni-app
bianshaopeng20 小时前
android 原生加载pdf
android·pdf