LaunchedEffect() 无法重新启动

LaunchedEffect() cannot be relaunched

here 阅读此 LaunchedEffect take a variable number of keys as a parameter that are used to restart the effect whenever one of those keys changes.

我正在尝试通过更改密钥并检查 LaunchEffect 是否会重新启动。

这是我的代码:


private const val SplashWaitTime: Long = 2000

@Composable
fun LandingScreen(modifier: Modifier = Modifier, onTimeout: () -> Unit) {
    Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
        val currentOnTimeout by rememberUpdatedState(onTimeout)
        var key = 1
        LaunchedEffect(key) {
            Log.w("xx", "Launched effect is running key = $key")
            delay(SplashWaitTime)
            key = 2
            currentOnTimeout()
        }
        key = 3
        Image(painterResource(id = R.drawable.ic_crane_drawer), contentDescription = null)
    }
}

使用上面的代码,运行打开应用程序,我可以看到 Logcat.

中只有一个日志打印

即:

2022-05-08 18:42:35.951 20101-20101/androidx.compose.samples.crane W/xx: Launched effect is running key = 3

所以问题是:

  1. 如何确保 LaunchEffect 可以 运行 且 key = 1 首先?然后使用 key = 3.
  2. 重新启动
  3. 为什么 key = 2 不会触发 LaunchEffect 重新启动?

更新:

我的Android工作室:

Android Studio Chipmunk | 2021.2.1 Beta 4
Build #AI-212.5712.43.2112.8233820, built on March 1, 2022
Runtime version: 11.0.12+0-b1504.28-7817840 x86_64

我的撰写版本是1.1.1


更新 2: 我接受@MARSK 的回答。只是想在这里放更多信息以供其他伙伴参考。

我正在遵循代码路径这个 codelab。所以上面的代码是其中的一部分,我认为这里的重点是:

  1. 键应该定义为状态,如remember { mutableStateOf(1) }
  2. 预期的结果应该是无限循环,因为在 LaunchedEffect 块内部,语句 key = 2 将始终触发重组。

为什么我无法弄清楚它是如何工作的,而且我只打印了一份日志?那是因为 onTimeout 回调,从 LandingScreen 可组合项的父级,该函数将从树中完全删除这个(LandingScreen)可组合项,因此它没有任何机会被重新组合。

var key by remember { mutableStateOf (1) }

以上是它工作的关键。代码按预期工作。这是详细信息。

当你运行这个简单的代码块

var key by remember { mutableStateOf(1) }
LaunchedEffect(key) {    
  Log.e("xx", "Running key $key")
  delay(2000)
  key = 2
}

key = 3

代码运行s为:key初始化为1,效果运行s为key = 1的值,但是,as正确(我希望)指出根据下面评论中的 OP,LaunchedEffect 需要一些时间来生成协程,这足以让控件转移到下一条语句并将键的值更改为 3。这确保了永远不会记录 key 的值为 1。

此后延迟两秒,键的值变为 1,触发可组合项的整个重组,这意味着整个可组合项应 re-execute。这是怎么回事:

密钥保持为 2,因为我们使用 remember LaunchedEffect 被调用时值为 2,但同样,由于 coroutine-spawning 时间,密钥被修改为 3,这与 2 不同,RE-TRIGGERS 重组,因此,永远不会记录2,但只记录 3,因为那是 key 现在的值。同样,之前的LaunchedEffect并没有被取消,当它的two-second延迟结束后,它会再次将值切换为2,它立即移回3,因为这个作文。因此,您将始终如一地获得 Running 3 的日志,两个日志在时间上非常接近,而下一个日志将被 two-second 间隔分隔。

这就是你得到的

17:28:43.241 : Running key 3
17:28:43.269 : Running key 3
17:28:45.298 : Running key 3
17:28:45.318 : Running key 3
17:28:47.356 : Running key 3
17:28:47.374 : Running key 3
17:28:49.422 : Running key 3
17:28:49.443 : Running key 3
17:28:51.501 : Running key 3
17:28:51.534 : Running key 3
17:28:53.591 : Running key 3
17:28:53.607 : Running key 3
17:28:55.649 : Running key 3
17:28:55.689 : Running key 3

现在,如果您希望正确记录调用效果的值,则需要将其存储在入口点本身。像这样,

var key by remember { mutableStateOf(1) }
var keyStore: Int
LaunchedEffect(key.also { keyStore = key }) {
    "Running key $keyStore".log()
    delay(2000)
    key = 2
}
key = 3

这会生成正确的运行时间日志

17:34:13.790 : Running key 1
17:34:13.821 : Running key 3
17:34:15.847 : Running key 2
17:34:15.862 : Running key 3
17:34:17.903 : Running key 2
17:34:17.922 : Running key 3
17:34:19.982 : Running key 2
17:34:20.015 : Running key 3
17:34:22.055 : Running key 2
17:34:22.073 : Running key 3
17:34:24.135 : Running key 2
17:34:24.154 : Running key 3