Jetpack Compose 记住实际上做了什么,它是如何工作的?

新手上路,请多包涵

查看代码实验室的基本教程,有一个片段可以在单击时增加按钮上的计数器

@Composable
fun MyScreenContent(names: List<String> = listOf("Android", "there")) {
    val counterState = remember { mutableStateOf(0) }

    Column(modifier = Modifier.fillMaxHeight()) {
        Column(modifier = Modifier.weight(1f)) {
            for (name in names) {
                Greeting(name = name)
                Divider(color = Color.Black)
            }
        }
        Counter(
            count = counterState.value,
            updateCount = { newCount ->
                counterState.value = newCount
            }
        )
    }
}

@Composable
fun Counter(count: Int, updateCount: (Int) -> Unit) {
    Button(
        onClick = { updateCount(count + 1) },
        colors = ButtonConstants.defaultButtonColors(
            backgroundColor = if (count > 5) Color.Green else Color.White
        )
    ) {
        Text("I've been clicked $count times")
    }
}

很明显 remember { mutableStateOf(0) } 存储状态/值。我的问题是 remember 在幕后做了什么。使用 var count = remember { 0 }mutableStateOf(0)记得 不会增加值。

 fun MyScreenContent(names: List<String> = listOf("Android", "there")) {

    var count = remember { 0 }

    Column(modifier = Modifier.fillMaxHeight()) {
        Column(modifier = Modifier.weight(1f)) {
            for (name in names) {
                Greeting(name = name)
                Divider(color = Color.Black)
            }
        }
        Counter(
            count = count,
            updateCount = { newCount ->
                count = newCount
            }
        )
    }
}

上面的代码片段不会更新打印在 Text 上的值,记得只适用于 MutableState 吗?

原文由 Thracian 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 418
2 个回答

remember - 允许您记住先前重组调用的状态,仅此而已。因此,例如,如果您在初始运行时随机化颜色。随机颜色将被计算一次,并在需要重新组合时重复使用。

所以…记住 = 存储值以防万一调用 recompose。

现在第二件事是知道什么时候应该真正触发重新组合。并且可变状态会提供帮助。

mutablestate = 存储值,以防万一我更新值触发重组使用此数据的所有元素。

原文由 Daber 发布,翻译遵循 CC BY-SA 4.0 许可协议

要了解合成和重组的工作原理,您可以查看 Leland Richardson 撰写的 Under the hood of Jetpack Compose 文章,该文章很好地描述了内部工作原理, 这里 还有 youtube 视频。这个答案的大部分内容都使用文章作为参考,并从中引用了大部分内容。

Composer 的实现包含一个与 Gap Buffer 密切相关的数据结构。这种数据结构常用于文本编辑器。

间隙缓冲区表示具有当前索引或游标的集合。它是在内存中用平面数组实现的。该平面数组大于它所代表的数据集合,未使用的空间称为间隙。

Basically adding space near your Composable function slot table to be able to update UI dynamically with high costs since get , move , insert , and delete -- 是恒定时间操作,除了移动间隙。移动差距是 O(n) ,但这种情况并不经常发生,您需要更改所有 UI 结构,平均而言,UI 结构不会改变太多。

 @Composable
    fun Counter() {
     var count by remember { mutableStateOf(0) }
     Button(
       text="Count: $count",
       onPress={ count += 1 }
     )
    }

当编译器看到 Composable 注释时,它会插入额外的参数并调用函数的主体。首先,编译器添加对 composer.start 方法的调用,并将编译时生成的密钥整数传递给它。

 fun Counter($composer: Composer) {
 $composer.start(123)
 var count by remember($composer) { mutableStateOf(0) }
 Button(
   $composer,
   text="Count: $count",
   onPress={ count += 1 },
 )
 $composer.end()
}

当此作曲家执行时,它会执行以下操作:

  1. Composer.start 被调用并存储一个组对象
  2. 记住插入一个组对象
  3. 存储 mutableStateOf 返回的值,即状态实例。
  4. 按钮存储一个组,后面跟着它的每个参数。
  5. 最后我们到达了 composer.end。

在此处输入图像描述

数据结构现在包含组合中的所有对象,按执行顺序排列的整个树,有效地对树进行 深度优先遍历

所以 remember 需要存储一个 mutableState() 以从以前的合成中获取值,而 mutableState() 需要触发一个。

MutableState 接口使用 @Stable 注释

@Stable
interface MutableState<T> : State<T> {
    override var value: T
    operator fun component1(): T
    operator fun component2(): (T) -> Unit
}

Stable 用于向 compose 编译器传达关于特定类型或函数的行为方式的一些保证。

当应用于类或接口时,Stable 表示以下必须为真:

  1. equals 的结果将始终为相同的两个实例返回相同的结果。
  2. 当类型的公共属性发生变化时,组合将得到通知。
  3. 所有公共财产类型都是稳定的。当应用于函数或属性时,Stable]注解表示如果传入相同的参数,函数将返回相同的结果。这仅在参数和结果本身是稳定的、不可变的或原始的时才有意义。

此注释暗示的不变量用于组合编译器的优化,如果不满足上述假设,则具有未定义的行为。因此,除非确定满足这些条件,否则不应使用此注释。

另一个 带有描述 Compose 工作原理的视频的来源

原文由 Thracian 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题