定义
@Immutable
是一个注释,用于告诉 Compose 编译器此对象对于优化来说是不可变的,因此如果不使用它,可能会触发不必要的重新组合。
@Stable
是另一个注释,用于告诉 Compose 编译器此对象可能会发生变化,但当它发生变化时,Compose 运行时将收到通知。
如果你读到这里,可能还不太明白。所以需要更多解释...
Compose 指标报告
当您生成 Compose 指标报告 ,它会将对象标记为 stable
or 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) {
}
尽管上面的 Composable 接受 SomeViewState
其所有属性为 val
,但它仍然是 unstable
。你可能想知道为什么?这是因为在使用方面,你实际上可以将它与 一起使用 MutableList
,如下所示:
ShowSomething(SomeViewState(mutableListOf()))
因此,编译器必须将其标记为 unstable
.
因此在这种情况下,我们想要实现的是 stable
重新制作它们,以便对它们进行优化。
@Stable 和 @Immutable
有两种方法可以 stable
再次制作,分别是使用 @Stable
和 @Immutable
.
使用 @Stable
,如上所述,这意味着可以更改值,但是当它确实发生变化时,我们必须通知 Compose 编译器。实现方法是通过使用 mutableStateOf()
:
@Stable
class SomeViewState {
var isLoading by mutableStateOf(false)
}
使用 @Immutable
,意味着当您将数据传递到 Composable 时,您将始终生成数据的新副本,换句话说,您承诺数据是不可变的。从上面的例子来看:
@Immutable
data class SomeViewState {
val list: List<String>
}
@Composable
fun ShowSomething(data: SomeViewState) {
}
在使用 进行注释后 @Immutable
,在使用方面,您应该确保创建一个新列表,而不是直接改变列表。
示例 DO:
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!
}
}
为了更深入地了解,您可以阅读以下链接:
-
https://medium.com/androiddevelopers/jetpack-compose-stability-explained-79c10db270c8
-
https://chrisbanes.me/posts/composable-metrics/