主要翻译自官方文档Side-effects in Compose,并不是直译,有些细微调整。
一个副作用是指发生在composable函数范围之外的应用状态的一个变化。由于composable函数的生命周期和诸如不可预测的重组,以不同的顺序执行composable的重组,或者重组可能会被跳过等性质,理论上composable应该要是无副作用的。
然而,有些时候副作用是必要的,例如,触发一些诸如显示一个非干扰性提示(snackbar)或者在一定状态条件下跳转到另一个页面,等的一次性的事件时。这些行为应该在一个能够感知composable生命周期的可控的环境中调用。在本文中,你将学习Jetpack Compose提供的几种不同的副作用函数(side effect APIs)。
副作用的具体使用场景
如在文章降Compose十八掌之『潜龙勿用』| Thinking in Compose中提到的,composables应该尽可能的做到无副作用。当需要对应用状态进行修改时,应该使用副作用API,以便副作用函数以可预测的方式运行。
关键点: 一个作用(effect)是指一个composable函数不会生成UI元素,而是当组合完成时生成副作用。
由于Compose中有多种作用,很容易被滥用。要确保在副作用中做的事情是UI相关的并且没有违反『单一数据流原则』。
注意: 一个可响应的UI应该是异步的,Jetpack Compose解决异步的办法是在API级别结合协程而不是使用回调。想要了解更多的协程知识,可以参看之前的文章。
LaunchedEffect:在composable的作用域内运行挂起函数
想要在一个composable的生命周期中执行操作并且需要调用挂起函数,就可以使用LaunchedEffect。当LaunchedEffect进入组合时,它会使用作为参数传入的代码块来启动一个协程。如果LaucnhedEffect离开了组合协程会被取消。如果因不同的key LaunchedEffect被重组了(副作用的重启机制会在后面进行讲解),运行中的协程会被取消掉,一个新的协程会被启动以运行新的挂起函数。
例如,一个可调节延迟的脉冲式透明度的动画:
// 变化的速率可以调节,可以加快动画(减少间隔)
var pulseRateMs by remember { mutableStateOf(3000L) }
val alpha = remember { Animatable(1f) }
LaunchedEffect(pulseRateMs) { // 速度作为key,这样速度变化时,会重启副作用,动画也会重启
while (isActive) {
delay(pulseRateMs) // 一定间隔之后显示脉冲动画
alpha.animateTo(0f)
alpha.animateTo(1f)
}
}
在上面的代码中,动画使用了挂起函数delay来等待一定的时间。然后,它依次使用animateTo展现动画到不可见,再到可见。并在composable的生命周期中不断重复。
rememberCoroutineScope:获取一个可以在composable之外启动协程的可感知组合的协程作用域
因为LaunchedEffect是一个composable函数,所以它只能在其他composable函数中调用。如果想要在composable作用域之外启动协程,但又希望限制协程在一定的范围内,以便能在离开组合时协程自动被取消,可以使用