定义
@Immutable
是一个注解,用于告诉Compose编译器此对象为不可变对象,以便进行优化。如果不使用它,则可能会触发不必要的重新组合。
@Stable
是另一个注解,用于告诉Compose编译器此对象可能会更改,但当它更改时,Compose运行时将会被通知。
如果你读到这里还没有理解,那么以下是更详细的说明...
Compose度量报告
当您生成Compose度量报告(https://github.com/androidx/androidx/blob/08c6116/compose/compiler/design/compiler-metrics.md)时,它会标记物品为stable
或unstable
。对于unstable
的对象,Compose编译器无法确定对象是否被修改,因此需要重新触发组合。以下是报告的两个片段:
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun SomeClass1(
stable modifier: Modifier? = @static Companion
)
restartable scheme("[androidx.compose.ui.UiComposable]") fun SomeClass2(
stable modifier: Modifier? = @static Companion
stable title: String
unstable list: List<User>
stable onClicked: Function1<User>, Unit>
)
可跳过是期望的!
在SomeClass1
的情况下,它被标记为skippable
,因为所有参数都被标记为stable
。对于SomeClass2
,它没有被标记为skippable
,因为它有一个list
属性是unstable
。
当它被标记为skippable
时,这是一件好事,因为Compose编译器可以尽可能地跳过重组并且更加优化。
什么情况下无法标记为可跳过?
通常Compose编译器足够聪明,能够推断出什么是stable
和什么是unstable
。在Compose编译器无法确定稳定性的情况下是可变对象,例如包含var
属性的类。
class SomeViewState {
var isLoading: Boolean
}
如果遇到像Collection
这样的类,例如List
,即使接口为看起来不可变的List
,它实际上可能是可变列表,那么就会失败地决定稳定性。 例如:
data class SomeViewState {
val list: List<String>
}
@Composable
fun ShowSomething(data: SomeViewState) {
}
尽管上面的可组合部件接受所有属性为val
的SomeViewState
,但它仍然是不稳定的
。你可能会想为什么?那是因为在使用方面,你实际上可以像这样使用它:MutableList
。ShowSomething(SomeViewState(mutableListOf()))
因此,编译器将不得不将其标记为不稳定
。
因此,像这样的情况下,我们想要实现的目标是使它们重新变得稳定
,以便进行优化。
@Stable和@Immutable
有两种方法可以使其重新变得稳定
,分别是使用@Stable
和@Immutable
。
使用@Stable
,如上所述,它意味着该值可以更改,但当它发生更改时,我们必须通知Compose编译器。完成方式是通过使用mutableStateOf()
:
@Stable
class SomeViewState {
var someFlag by mutableStateOf(false)
}
使用 @Immutable
,这意味着当您传递给 Composable 时,您将始终创建数据的新副本,换句话说,您承诺您的数据是不可变的。从上面的例子:
@Immutable
data class SomeViewState {
val list: List<String>
}
@Composable
fun ShowSomething(data: SomeViewState) {
}
在使用@Immutable
进行注释后,在使用端,您应该确保创建一个新的列表而不是直接更改原始列表。
示例:
class ViewModel {
val state: SomeViewState = SomeViewState(listOf())
fun removeLastItem() {
val newList = state.list.toMutableList().apply {
removeLast()
}
state = state.copy(
list = newList
)
}
}
例子:不要这样做。
class ViewModel {
val state: SomeViewState = SomeViewState(mutableListOf())
fun removeLastItem() {
state.list.removeLast() // <=== you violate your promise of @Immutable!
}
}
欲深入了解,可查阅以下链接:
编译器将两者视为相同,但
@Immutable
表示该值永远不会改变。@Stable
表示该值是可观测的,如果它发生了改变,则会通知监听器。@Immutable
和@Stable
可以帮助对象避免触发重新组合。例如,请参见https://medium.com/androiddevelopers/jetpack-compose-stability-explained-79c10db270c8或https://www.jetpackcompose.app/articles/how-can-I-debug-recompositions-in-jetpack-compose。 - Ted HoppColor
被标记为@Stable
而不是@Immutable
? - Mark