我在 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 对象的新实例。每次击键都会发生这种情况,并且在具有许多字段的较大应用程序或表单中,这可能会变得低效。
我的问题:
如果有一种方法,我们可以直接更改数据类值并自动触发重组,而不是复制对象,该怎么办?
对于视图,建议不要实例化新对象,尤其是多次调用的
onDraw
函数。
但是,在jetpack compose中,不鼓励使用强跳过,因为如果组合发生在具有不稳定参数的作用域函数中,对于数据类不可变参数,或者来自外部或另一个不扩展组合编译器的模块的不稳定类,它会触发重组.
https://developer.android.com/develop/ui/compose/performance/stability#mutable-objects
@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 注释的变量,如果另一个可组合项触发重组,则可以防止重组,并在您的汽车属性发生变化时触发重组,例如
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()
策略,您可以通过分配相同的对象来触发重组。
它适用于任何类,您可以通过将计数器值设置为相同的值来触发重组。
这种方法广泛应用于 RememberXState 类和 Google 的 Jetsnack 示例中。
@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)
}
您只需将类划分为应触发重组的属性和不需要的其他属性,因为触发重组时它们也会发生更改。