【问题标题】:Why is LaunchedEffect(true) suspicious?为什么 LaunchedEffect(true) 可疑?
【发布时间】:2022-10-07 11:26:25
【问题描述】:

我正在使用 compose 实现 MVI。为了让我遵循正确的事件循环,我需要通过我的视图模型传播点击事件,然后观察副作用。我查看了一些实现,它们都使用LaunchedEffect(true) 来观察副作用并采取行动。

我有一个类似的设置,例如:

@Composable
fun HelloComposeScreen(
    viewModel: MyViewModel = hiltViewModel(),
    onClickedNext: () -> Unit
) {
    LaunchedEffect(true) {
        viewModel.sideEffect.collectLatest { sideEffect ->
            when (sideEffect) {
                DashboardSideEffect.CreateParty -> onClickedNext()
            }
        }
    }
    Button(
        onClick = { viewModel.onEvent(UserEvent.ClickedButton)},
    ) {
        Text(\"Click Me\")
    }
}

这导致我将LaunchedEffect(true) 用于任何具有导航或一次性事件的屏幕,但official documentation 有此警告

警告:LaunchedEffect(true) 与 while(true) 一样可疑。即使它有有效的用例,也请始终暂停并确保这是您所需要的。

我的问题是:

  • LaunchedEffect 究竟何时被取消?文档说它与调用站点的生命周期相匹配。这就是这种情况下的组成吗?
  • 考虑到官方文档有警告吗?我不应该使用这个LaunchedEffect(true) 设置来观察我的项目的副作用吗?什么是替代方案?
  • 请不要在一个问题中提出多个问题。其他人很难找到所需的答案。见How do I ask a good question。我已经回答了你关于LaunchedEffect 的问题,关于MVI 的部分超出了这个问题的范围,你可以创建另一个。
  • 我把这些问题放在一起,因为它们是相互关联的。我可以删除关于 MVI 的最后一个,但如果我单独询问第二个可能会被认为是固执己见。
  • Google 在produceState 中使用LaunchedEffect(Unit) {,因此可以轻松地完全忽略此警告,尽管您应该始终确保您实际上不需要任何键来取消和重新启动协程。

标签: android android-jetpack-compose


【解决方案1】:

LaunchedEffect 与它的协程一起被取消,有两种变体:

  1. 传递的key 参数已更改 - 在这种情况下,当前的LaunchedEffect 将被取消并创建一个新的。
  2. LaunchedEffect 从生命树中删除,例如,如果您将它(或其任何级别的父级)放在 if 块中并且条件变为 false

    如果您不需要传递任何应该重新启动LaunchedEffectkey,则可以传递Unit。任何其他常量,例如您的情况下的true,都被认为是可疑的,因为它不能在运行时更改,但对于任何编码人员来说可能看起来像复杂的逻辑。

【讨论】:

  • 对于您的第二个子弹“LaunchedEffect 已从生命树中删除..”。您说,当包裹在条件中时,它会从组合中删除,但在我的情况下,它没有包裹在条件中。那么它究竟什么时候会被移除呢?是HelloComposeScreen 离开作文的时候吗?
  • @Naveed 是的,每当您的可组合离开组合时。无论是通过条件还是导航或其他任何方式
  • LaunchedEffect(true) 和 LaunchedEffect(Unit) 具有相同的行为。 Android 文档:为了创建与调用站点的生命周期相匹配的效果,将像 Unit 或 true 这样的永不改变的常量作为参数传递。
  • @DissidentDev 是的,就像您可以使用 false 或任何其他常量一样,但是对于阅读您的代码的人来说,它可能看起来像某种逻辑,因此使用 Unit 更安全。这就是为什么我的回答有关。
【解决方案2】:

LacunchedEffect 是一个 Composable 函数,它在 coroutineScope 中运行协程。

coroutineScope 将在两种情况下被取消并重新启动:

  1. 当传递的keysLaunchedEffect 被更改时。将传递的键从值x 更改为y 会取消当前的coroutineScope,然后使用新传递的keys 再次启动LaunchedEffect 内的代码块。
  2. LaucnhedEffect 退出组合时。这意味着如果LaucnhedEffect 没有重新组合,则在以后的组合中。例如,因为它位于被评估为 falseif 语句中,或者如果组合树中的父可组合项之一退出组合。

    例子:

        @Composable
        fun MyComposable(authorId: Int, showReadMore: Boolean) {
        // ... logic....
        
        // When showReadMore is false, the latest LaunchedEffect composable exits the composition (the coroutineScope will be cancelled)
        if (showReadMore) {
            // Changing the value of authorId when showReadMore is true, cancels the coroutineScope and launch the block again.
            LaunchedEffect(authorId) {
                // Get more info of the author using suspend function
                // Since we use LaunchedEffect to run suspend function(s) inside
            }
        }
    }
    

    对于第二个问题:传递 falsetrue12Unit 之类的任何值都会得到相同的结果。传递Unit 使代码更有意义且更易于阅读,因为它指示void,这意味着我们不关心在第一种情况下(当keys 更改时)重新启动coroutineScope,因为键是@987654346 @。

【讨论】:

    猜你喜欢
    • 2023-02-07
    • 1970-01-01
    • 2013-02-02
    • 2016-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-29
    相关资源
    最近更新 更多