State
State, MutableState
compose 中, 系统需要监听值的变化, 进行自动重组, 普通对象无法监听变化, 需要使用 State, MutableState
val count = mutableIntStateOf(1)
remember
compose 重组时, 由于 state 声明在函数体中, 会被重新初始化, 为了保证值不变, 加上 remember {}, 保证在重组时, 值不变
val count = remember { mutableIntStateOf(0) }
由于直接用 remember{} 获取的是 State 对象, 使用其 value 值时不方便; 借助 by 委托语法, 直接赋值, 方便使用
var count2 by remember { mutableStateOf(10) }
rememberSaveable
remember 解决了重组过程中的值重新初始化的问题, 但是当 Activity 横竖屏切换时, 依旧会重新初始化; Compose 内部使用 rememberSaveable 来解决
val count3 by rememberSaveable { mutableIntStateOf(1) }
Stateless, Stateful, 状态管理
状态上提
StateHolder
viewModel
LiveData, RxJava, Flow 转 State
| 转换方法 | 依赖 |
|---|---|
|
LiveData.observeAsState() |
androidx.compose:runtime-livedata |
| Flow.collectAsState() | |
| Observable.subscribeAsState() | androidx.compose: runtime-rxjava3 |
深层嵌套传值
创建 CompositionLocal
val CountLocalContent = compositionLocalOf(neverEqualPolicy()){
100
}
声明 Provider, 提供值
CompositionLocalProvider(CountLocalContent.provides(1001 + count2)) {
CompositionLocalProvider(CountLocalContent.provides(1101 + count2)) {
CountText(count = count2)
}
}
在子 Composeable 中, 获取值
@Composable
fun CountLocalText() {
val countLocal = CountLocalContent.current
Text(
text = "CountLocalText count Text $countLocal "
)
}
副作用
和 React 类似, Compose 重组, 方法体会重复执行, 但是部分操作并不需要每次都执行; 使用 Effect 去避免这种情况,
SideEffect
在重组成功时执行; Compose 会有多次重组, 但不一定每次都成功; SideEffect 在重组成功时执行
- 重组成功时执行
- 可能执行多次
DisposableEffect
和 React 中的 useEffect 类似, 在指定数据发生变化时执行; 如果传入 Unit 则在 Composeable 整个流程中, 只执行一次
DisposableEffect(vararg keys: Any?) {
// register(callback)
onDispose {
// unregister(callback)
}
}
LaunchedEffect
在 Composeable 中启动协程; 在进入 composition 阶段执行, 在离开 composition 时, 自动取消; 也可以传入 key, 监听值变化时执行
LaunchedEffect(vararg keys: Any?) {
// do Something async
}
rememberCoroutineScope
LaunchedEffect 只能在 Compose 函数中执行; 如果在非 Composeable 中执行协程 (eg. Button 的点击事件中), 则可以由 rememberCoroutineScope 获取协程的 scope
@Composable fun Test() {
val scope = rememberCoroutineScope()
Button( onClick = {
scope.launch { // do something }
} ) {
Text("click me")
}
}
rememberUpdatedState
一般和 LaunchEffect 和 DisposableEffect 一起使用;
- 当
LaunchEffect不监听任何 state 时, 且内部使用传入Composable参数时 - 该
Composeable状态变化, 重组传入新值, 由于LaunchedEffect和生命周期绑定, 不会重建 -
LaunchedEffect获取传入的参数值, 永远是第一次的值 - 使用
rememberUpdatedState则可以获取Composeable重组后的新值
React也有类似的问题, 当props变化时, 内部并不会获取新的值
@Composable
fun CountText(count: Int) {
val count by rememberUpdatedState(newValue = count)
// 如果不使用 rememberUpdatedState, 底下LaunchedEffect始终获取的是第一次进入composition阶段的值
LaunchedEffect(Unit) {
Log.d("zy", "------ LaunchedEffect-- start $count")
for (i in 1..10) {
delay(1 * 1000L)
Log.d("zy", "------ LaunchedEffect- $i >>> $count")
}
Log.d("zy", "------ LaunchedEffect-- end $count")
}
Text(
text = "count Text $count "
)
}
snapshotFlow
将 State 值的变化, 转换为 Flow 冷流
上面的 rememberUpdateState, 在 LaunchedEffect 绑定 Composeable 生命周期时, 只能获取到最新的值, snapshotFlow 可以获取到每一次值的变化
val count by rememberUpdatedState(newValue = count)
LaunchedEffect(Unit) {
snapshotFlow { count }
.collect {
delay(1 * 100L)
Log.d("zy", "------ LaunchedEffect-- snapshotFlow $count")
}
}
derivedStateOf
根据一个 State 派生出另一个 State;
仅在两个 State 变化状态不同步时使用, 如果两个 State 是同时变化, 就直接使用 源State 更新 UI 了, 使用 derivedStateOf 纯粹添加性能开销
@Composable
fun MessageList(messages: List<Message>) {
Box {
val listState = rememberLazyListState()
LazyColumn(state = listState) {
// ...
}
// 根据 listState 派生出 showButton;
// showButton 变化频率低, 使用 derivedStateOf,部分UI可以不用重组, 可以降低性能开销
val showButton by remember {
derivedStateOf {
listState.firstVisibleItemIndex > 0
}
}
AnimatedVisibility(visible = showButton) {
ScrollToTopButton()
}
}
}
produceState
开启一个协程, 将普通数据, 转换为 State; eg. 将 Flow, LiveData 之类的数据, 转换成 State
内部有一个 awaitDispose 方法, 可以在离开 Composition 时做清理工作
val countChange = produceState(initialValue = 1) {
value = count + 1
awaitDispose {
// 做清理工作
}
}
