Compose 中的附带效应

附带效应是指发生在可组合函数作用域之外的应用状态的变化。由于可组合项的生命周期和属性(例如不可预测的重组、以不同顺序执行可组合项的重组或可以舍弃的重组),可组合项在理想情况下应该是无附带效应的

不过,有时附带效应是必要的,例如,触发一次性事件(例如显示信息提示控件),或在满足特定状态条件时进入另一个屏幕。这些操作应从能感知可组合项生命周期的受控环境中调用。在本页中,您将了解 Jetpack Compose 提供的不同附带效应 API。

状态和效应用例

Compose 编程思想文档中所述,可组合项应该没有附带效应。如果您需要更改应用的状态(如管理状态文档中所述),您应该使用 Effect API,以便以可预测的方式执行这些附带效应

由于效应会在 Compose 中带来各种可能性,所以很容易过度使用。确保您在其中完成的工作与界面相关,并且不会破坏单向数据流,如管理状态文档中所述。

LaunchedEffect:在某个可组合项的作用域内运行挂起函数

如需在可组合项的生命周期内执行工作并能够调用挂起函数,请使用 LaunchedEffect 可组合项。当 LaunchedEffect 进入组合时,它会启动一个协程,并将代码块作为参数传递。如果 LaunchedEffect 退出组合,协程将取消。如果使用不同的键重组 LaunchedEffect(请参阅下方的重启效应部分),系统将取消现有协程,并在新的协程中启动新的挂起函数。

例如,以下动画会以可配置的延迟时间脉冲式变化 Alpha 值:

// Allow the pulse rate to be configured, so it can be sped up if the user is running
// out of time
var pulseRateMs by remember { mutableLongStateOf(3000L) }
val alpha = remember { Animatable(1f) }
LaunchedEffect(pulseRateMs) { // Restart the effect when the pulse rate changes
    while (isActive) {
        delay(pulseRateMs) // Pulse the alpha every pulseRateMs to alert the user
        alpha.animateTo(0f)
        alpha.animateTo(1f)
    }
}

在上面的代码中,动画使用挂起函数 delay 等待设定的时间。然后,它使用 animateTo 将 Alpha 依次动画化为零,然后再动画化回来。 此操作将在可组合项的整个生命周期内重复执行。

rememberCoroutineScope:获取组合感知作用域,以便在可组合项外启动协程

由于 LaunchedEffect 是可组合函数,因此只能在其他可组合函数中使用。为了在可组合项外启动协程,但存在作用域限制,以便协程在退出组合后自动取消,请使用 rememberCoroutineScope。 此外,如果您需要手动控制一个或多个协程的生命周期,请使用 rememberCoroutineScope,例如在用户事件发生时取消动画。

rememberCoroutineScope 是一个可组合函数,会返回一个 CoroutineScope,该 CoroutineScope 绑定到调用它的组合点。调用退出组合后,作用域将取消。

根据上面的示例,当用户点按 Button 时,您可以使用以下代码显示 Snackbar

@Composable
fun MoviesScreen(snackbarHostState: SnackbarHostState) {

    // Creates a CoroutineScope bound to the MoviesScreen's lifecycle
    val scope = rememberCoroutineScope()

    Scaffold(
        snackbarHost = {
            SnackbarHost(hostState = snackbarHostState)
        }
    ) { contentPadding ->
        Column(Modifier.padding(contentPadding)) {
            Button(
                onClick = {
                    // Create a new coroutine in the event handler to show a snackbar
                    scope.launch {
                        snackbarHostState.showSnackbar("Something happened!")
                    }
                }
            ) {
                Text("Press me")
            }
        }
    }
}

rememberUpdatedState:在效应中引用某个值,该效应在值改变时不应重启

当其中一个键参数发生变化时,