通常情况下状态发生改变时想要更新UI,我们会使用 var value by remeber { mutableStateOf() }来创建一个可观察的状态数据。当state的数据发生改变以后,使用了state数据组件会发生重组,从而实现刷新。 但有些情况下,我们想要实现在可组合函数中执行一次网络请求,根据请求结果改变界面状态,或者执行挂起函数再改变界面状态。此时需要用到附带效应。
1.LaunchedEffect
launchedEffect主要作用是让你在Composable作用域里实现协程工作,来完成异步、耗时操作、网络请求等
kotlin
@Composable
fun LunchedEffectTest(){
var value by remeber{ mutableStateOf() }
LaunchedEffect(key){ //key如果没有发生变化,则重组中不会启动新的协程
while(true){
delay(100)
value++
}
}
Text(text='${value}')
}
2.rememberCoroutineScope
LanchedEffect只能在composable作用域内调用,无法在按键点击回调等地方调用。rememberCoroutineScope可以在这些地方调用。
kotlin
@Composable
fun rememberCoroutineScopeTest(){
val scope = rememberCoroutineScope()
Button(
onclick={
scope.launch{
//模拟网络请求
}
}{
Text("请求数据")
}
)
}
3.rememberUpdateState 只是辅助LaunchedEffect向外部(其他composeable方法)更新状态功能,用于在不同compose方法下LaunchedEffect的数据更新时提供更新数据。
kotlin
@Composable
fun RemeberUpdateStateTest(){
var name by remember { mutableStateOf("") }
RemeberUpdateStateUI(name = name,onValueChanged = {
name = it
})
}
@Composable
fun RemeberUpdateStateUI(name:String,onValueChanged:(String) -> Unit){
val nameUpdateState = remmberUpdateState(name)
LaunchedEffect(key){ //key保持一致可以为true
delay(1000)
//Log.d("RemeberUpdateStateUI","$name") //正常情况下如果这里输入框输入完以后,这里最后结果并不是预期的,比如输入框111222,但是这里可能1000毫秒以后,name还是111,所以要用remeberUpdateState包装一下。
//nameUpdateState 包装后的结果,这里获取的nameUpdateState对象中的值就是111222
}
OutLinedTextField(
value = name,
onValueChange = onValueChanged
)
}
工作原理分析: rememberUpdatedState 的内部逻辑可以简化为以下核心思想:
- 创建一个持久的
MutableState容器 :这个容器通过remember在重组中保持不变。 - 在每次重组时更新该容器的值 :无论
key是否变化,只要可组合函数被调用,它就会将newValue更新到内部的MutableState中。 - 返回该容器的引用:效应内部通过这个稳定的引用,读取到的永远是最新存入的值。
其内部实现类似于以下的简化逻辑(概念模型):
kotlin
@Composable
fun <T> rememberUpdatedState(newValue: T): State<T> = remember {
mutableStateOf(newValue)
}.apply { value = newValue }
// 注意:每次重组都会执行 `.apply { value = newValue }` 来更新值
4.SideEffect
SideEffect相当于DisposableEffect的简化,当不需要onDispose,不需要参数控制。compose操作非原子操作,重组中可能发生意外。
kotlin
@Composable
fun sideEffectTest(user:User){
val count = remember { mutableStateOf(0) }
SideEffect{
count = user.number
}
Text(text = count.value.toString,modifier = Modifier.clickable={
count.value ++
})
}
5.DispoableEffect
DispoableEffect必须添加一个onDispose 方法,此方法一般用于处理资源清理和释放。另外DisposableEffect也带参数意义和LaunchedEffect的参数一样。
kotlin
@Composable
fun DisposableEffectTest(dispatcher:OnBackPressDispather){
val backCallback = remember {
object:OnBackPressCallback(true){
override fun handleOnBackPressed(){
//TODO
}
}
}
DisposableEffect(dispatcher){
dispatcher.addCallback(backCallback)
onDispose{
backCallback.remove()
}
}
}