在Android Compose中, 如果需要在协程LaunchedEffect中读取某个状态的最新状态值,而不需要重启协程需要使用到rememberUpdatedState。
下面例子在LaunchedEffect下,对delay期间状态a的值变化进行观测。
1. 使用 rememberUpdatedState
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
var a by remember { mutableStateOf(1) }
Button(onClick = { a = 2 }) {
LandingScreen(a)
}
}
}
}
@Composable
fun LandingScreen(a:Int) {
val current by rememberUpdatedState(a)
Log.i("LandingScreen","a = $a")
LaunchedEffect(Unit) {
Log.i("LandingScreen","LaunchedEffect start")
delay(3000)
Log.i("LandingScreen","LaunchedEffect current=$current")
}
Text(text = "a=$a")
}
//应用启动后,3秒内点击Button,log输出结果:
a = 1
LaunchedEffect start
a = 2
LaunchedEffect current=2
从结果中可以看到当前状态值,显示为最新的状态值2,并且不重启LaunchedEffect。
2. 如果不使用rememberUpdatedState,使用remember
@Composable
fun LandingScreen(a:Int) {
val current by remember{ mutableStateOf(a) }
Log.i("LandingScreen","a = $a")
LaunchedEffect(Unit) {
Log.i("LandingScreen","LaunchedEffect start")
delay(3000)
Log.i("LandingScreen","LaunchedEffect current=$current")
}
Text(text = "a=$a")
}
//应用启动后,3秒内点击Button,log输出结果:
a = 1
LaunchedEffect start
a = 2
LaunchedEffect current=1 //current状态对象还是之前的值没有更新
从结果中可以看到当前状态值,还是显示旧的值1,并且不重启LaunchedEffect。
3. 如果不使用rememberUpdatedState,使用key标识remember和LaunchedEffect
@Composable
fun LandingScreen(a:Int) {
val current by remember(key1 = a){ mutableStateOf(a) }
Log.i("LandingScreen","a = $a")
LaunchedEffect(key1 = a) {
Log.i("LandingScreen","LaunchedEffect start")
delay(3000)
Log.i("LandingScreen","LaunchedEffect current=$current")
}
Text(text = "a=$a")
}
//应用启动后,3秒内点击Button,log输出结果:
a = 1
LaunchedEffect start
a = 2
LaunchedEffect start
LaunchedEffect current=2
从结果中可以看到当前状态值,显示为最新的状态值2,并且重启了LaunchedEffect。使用key,当状态值变更重组时,会取消当前LaunchedEffect,重启LaunchedEffect读取变更后的状态值。
rememberUpdatedState实现
@Composable
fun <T> rememberUpdatedState(newValue: T): State<T> = remember {
mutableStateOf(newValue)
}.apply { value = newValue }
可以看到,它只是对状态值进行重新赋值了。当状态值变更重组时,会重新调用可组合项函数,并且调用一次apply。remember保存的状态对象不会改变,但它的值更新了。因此,LaunchedEffect(Unit){}会读取到最新的值。