compose中SideEffect涉及的闭包捕获解释
问题
这里只从kotlin语法的角度解释上述代码中LaunchedEffect的lambda参数捕获onTimeout发生在什么时候?
@Composable
fun Timer(onTimeout: () -> Unit) {
LaunchedEffect(key1 = Unit) {
delay(500L)
onTimeout()
}
Text("计时器正在运行...")
}
详细解释 (Kotlin/Lambda 捕获机制)
-
Lambda 闭包 (Closure) 的创建:
- 在 Kotlin 中,当您定义一个 lambda 表达式(即
LaunchedEffect { ... }中的{ delay(500L); onTimeout() }部分)时,如果这个 lambda 引用了它外部作用域中的变量(在这个例子中是onTimeout: () -> Unit参数),那么这个 lambda 就会形成一个闭包 (Closure)。 - 这个闭包会捕获它所引用的外部变量。
- 在 Kotlin 中,当您定义一个 lambda 表达式(即
-
捕获的时机:
Timer函数是@Composable函数,它在每次重组时都会被执行。LaunchedEffect是一个函数调用。当Timer首次执行并调用LaunchedEffect时,Compose 框架会判断 Key (Unit) 是否需要启动新的协程。- 因为这是第一次调用,
LaunchedEffect决定启动一个协程。 - 在启动协程之前,
LaunchedEffect的 lambda 参数(即协程体)被实例化。在实例化这个 lambda 闭包对象时,它会捕获当时Timer函数参数列表中onTimeout所指向的那个函数引用。
-
引用不变性 (Reference Immutability):
- Compose 函数的参数,例如
onTimeout,在单次函数执行的范围内是不可变的(就像任何 Kotlin 函数的val参数一样)。 - 一旦
LaunchedEffect协程启动,它内部的onTimeout()调用,将永久指向协程启动时捕获的那个函数引用,即使在后续重组中,父级 Composable 传入了新的onTimeout函数引用,协程内部捕获的值也不会自动更新。
- Compose 函数的参数,例如
总结
从 Kotlin 语法角度:
onTimeout 的捕获发生在 Timer 函数体首次执行到 LaunchedEffect 被调用的这个瞬间。它捕获的是当时作为参数传入的 onTimeout 函数引用,并将其固化在 LaunchedEffect 协程的闭包作用域内。