是否有更有效的方法来更新 Jetpack Compose Kotlin 中的数据类属性?

问题描述 投票:0回答:1

我在 Jetpack Compose 中有一个代表汽车的数据类:

data class Car(
    val id: Int = 0,
    val brand: String = "",
    val model: String = "",
    val year: Int = 2020
)

在我的可组合项中,我根据 TextField 组件中的用户输入更新此数据类的品牌和型号属性。目前,每次用户输入内容时我都会使用 copy() 函数,如下所示:

@Composable
fun CarScreen() {
    var car by remember { mutableStateOf(Car()) }

    Column {
        TextField(
            value = car.brand,
            onValueChange = { newBrand ->
                car = car.copy(brand = newBrand) // Using `copy()`
            },
            label = { Text("Brand") }
        )

        TextField(
            value = car.model,
            onValueChange = { newModel ->
                car = car.copy(model = newModel) // Using `copy()`
            },
            label = { Text("Model") }
        )
    }
}

关心:

我担心在每次文本更改时调用 copy() 可能会导致性能问题,因为每次用户在 TextField 中键入内容时它都会创建 Car 对象的新实例。每次击键都会发生这种情况,并且在具有许多字段的较大应用程序或表单中,这可能会变得低效。

我的问题:

  1. 在每个 TextField 更改上调用 copy() 效率低下 Jetpack Compose 的性能如何?这个常量对象创建会 和重组会导致性能明显下降, 尤其是在数据较大的模型或者频繁输入的场景下?
  2. 有哪些更好的方法来处理频繁的文本输入更改 不必一直使用 copy(),同时仍然确保 Compose 可以在必要时重构 UI 吗?我想维持一个 反应式 UI,无需创建过多的对象。

如果有一种方法,我们可以直接更改数据类值并自动触发重组,而不是复制对象,该怎么办?

android kotlin android-jetpack-compose
1个回答
0
投票

对于视图,建议不要实例化新对象,尤其是多次调用的

onDraw
函数。

但是,在jetpack compose中,不鼓励使用强跳过,因为如果组合发生在具有不稳定参数的作用域函数中,对于数据类不可变参数,或者来自外部或另一个不扩展组合编译器的模块的不稳定类,它会触发重组.

https://developer.android.com/develop/ui/compose/performance/stability#mutable-objects

enter image description here

enter image description here

@Composable
fun ContactRow(contact: Contact, modifier: Modifier = Modifier) {
   var selected by remember { mutableStateOf(false) }

   Row(modifier) {
      ContactDetails(contact)
      ToggleButton(selected, onToggled = { selected = !selected })
   }
}

复制汽车等对象,甚至具有原始值或包含原始值或字符串的类的数据,没有明显的性能开销。您可能只想考虑您的数据类是否包含大数据,例如 Bitmap 或 Base64 格式的图像等。

但是您仍然想进一步优化它,您可以使用带有 @Stable 注释的变量,如果另一个可组合项触发重组,则可以防止重组,并在您的汽车属性发生变化时触发重组,例如

使用另一个 SnapshotMutationPolicy

MutableState 的默认策略是

structuralEqualityPolicy()
,它检查您设置的新
value
是否与前一个
==
相同。在数据类中 equals 由主构造函数的参数确定。

@Suppress("UNCHECKED_CAST")
fun <T> structuralEqualityPolicy(): SnapshotMutationPolicy<T> =
    StructuralEqualityPolicy as SnapshotMutationPolicy<T>

private object StructuralEqualityPolicy : SnapshotMutationPolicy<Any?> {
    override fun equivalent(a: Any?, b: Any?) = a == b

    override fun toString() = "StructuralEqualityPolicy"
}

通过更改此策略,即使使用相同的对象,您也可以触发重组,例如

@Stable
data class Car(
    val id: Int = 0,
    val brand: String = "",
    var model: String = "",
    val year: Int = 2020,
)

@Preview
@Composable
fun CarScreen() {
    var car by remember {
        mutableStateOf(
            value = Car(),
            policy = neverEqualPolicy()
        )
    }

    Column {
        TextField(
            value = car.brand,
            onValueChange = { newBrand ->
                car = car.copy(brand = newBrand) // Using `copy()`
            },
            label = { Text("Brand") }
        )

        TextField(
            value = car.model,
            onValueChange = { newModel ->
//                car = car.copy(model = newModel) // Using `copy()`

                car = car.apply {
                    model = newModel
                }
            },
            label = { Text("Model") }
        )

        Text("Car brand: ${car.brand}, model: ${car.model}")
    }
}

正如您所见,使用

neverEqualPolicy()
策略,您可以通过分配相同的对象来触发重组。

它适用于任何类,您可以通过将计数器值设置为相同的值来触发重组。

使用具有 MutableStates 的类

这种方法广泛应用于 RememberXState 类和 Google 的 Jetsnack 示例中。

ScrollStatejetsnack搜索状态

@Stable
class CarUiState(
    brand: String = "",
    model: String = "",
) {

    var id: Int = 0
    var year: Int = 2020

    var brand by mutableStateOf(brand)
    var model by mutableStateOf(model)
}

您只需将类划分为应触发重组的属性和不需要的其他属性,因为触发重组时它们也会发生更改。

© www.soinside.com 2019 - 2024. All rights reserved.