Android: lifecycleScope.launchWhenResumed {} 已弃用

8
我已经使用lifecyclesScope的launchWhenResumed很长一段时间了,但它似乎已经被弃用了。文档建议使用repeatOnLifecycle(),但我只想让代码运行一次,就像旧方法一样。

1
你要用它来做什么?是的,有所差别,但当收集一个热流(StateFlow)时,这个差别并不重要。根据你的使用情况,也许我们可以建议一个不同的替代方案。 - LordRaydenMK
也许我想显示一个toast,访问视图或任何需要从主线程访问的内容。 - user19514005
5个回答

8

下面的代码实现了与 viewLifeCycleOwner.launchWhenResumed 相同的功能。

lifecycleScope.launch {
    lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
        // do your work here
    }
}

更新

我进行了更多的研究,以了解代码背后发生了什么。

  • viewLifeCycleOwner.launchWhenResumed 是一种方法,可以在相关视图恢复时启动协程。当视图暂停或销毁时,协程将被取消。当您想要启动一个特定于视图生命周期的协程时,此方法非常有用。
  • lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) 是一种方法,可以根据相关 LifecycleOwner 的生命周期状态自动启动和停止协程。当 LifecycleOwner 恢复时,协程将启动。当 LifecycleOwner 暂停或销毁时,协程将被取消。当您想要启动与应用程序或组件的一般生命周期相关而不是特定于特定视图的协程时,此方法非常有用。

Google 建议使用 lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) 而不是 viewLifeCycleOwner.launchWhenResumed,以促进更有效和灵活的处理生命周期事件的方法。

其中之一的原因是它可以更有效地处理后台任务。当应用程序转到后台时,与视图相关联的LifecycleOwner仍然可以处于RESUMED状态,即使视图不可见。如果您在此情况下使用viewLifeCycleOwner.launchWhenResumed来启动协程,则该协程将继续运行,即使用户已移开视图,这可能会导致不必要的资源消耗。

另一方面,lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED)旨在在与协程相关联的LifecycleOwner暂停或销毁时自动停止协程。当应用程序转到后台时,通常会暂停或销毁与协程相关联的LifecycleOwner,这意味着协程将自动取消。


2
不幸的是,文档存在误导,因为此函数具有非常不同的功能。repeatOnLifecycle将会重复执行。launchWhenResumed则不会。 - Carson Holzheimer

3

只需创建绑定到生命周期范围的延迟作业,然后在您想要的时候启动它。

fun Lifecycle.performOn(
        state:Lifecycle.State, 
        context:CoroutineContext = EmptyCoroutineContext, 
        block: (suspend CoroutineScope.() -> Unit)
) {
    coroutineScope.launch {
        val job = launch(context, start = CoroutineStart.LAZY, block)
        withStateAtLeast(state) { job.start() }
    }
}

block(代码块)将绑定到作用域,并在状态到达时启动。但请记住,当您在块内调用函数时,挂起解决生命周期状态的时间可能会移动到STOP或DESTROY。

最好的方法是保持同步执行。
例如:withStateAtLeastwithCreatedwithStartedwithResumed


我认为这个问题与launchWhenStarted等类似,因为当您的生命周期已停止/暂停时,您的挂起块将继续运行,例如当当前活动移动到后堆栈时。 - bompf

2
在一个单独的文件或同一个文件中创建一个扩展函数。
fun Fragment.launchWhenResumed(callback: () -> Unit) {
    lifecycleScope.launch { lifecycle.withResumed(callback) }
}

使用方法:

launchWhenResumed { 
    // your Code..
}

注意:withResumed打开了一个不可挂起的块,因此您需要再次启动。 - Kibotu

0
如果你想使用像lifecycleScope.launchWhenResumed这样的功能, 我推荐你这样做:
fun LifecycleOwner.launchWhenResumed(block: suspend CoroutineScope.() -> Unit) {
    lifecycleScope.launch {
        repeatOnLifecycle(Lifecycle.State.RESUMED) {
            block()
            this@launch.cancel()
        }
    }
}

或者

inline fun LifecycleOwner.launchWhenResumed(
    retryTime: Int = 1,
    crossinline block: suspend CoroutineScope.() -> Unit
) {
    lifecycleScope.launch {
        var retryCount = 0
        repeatOnLifecycle(Lifecycle.State.RESUMED) {
            try {
                block()
                this@launch.cancel()
            } finally {
                if (retryTime != -1) {
                    retryCount += 1
                    if (retryCount >= retryTime) {
                        this@launch.cancel()
                    }
                }
            }
        }
    }
}

0

你可以在问题跟踪器评论中查看其中一个推荐的解决方案。

本质上,你需要考虑当你的挂起代码正在执行时,生命周期是否低于预期状态会发生什么。旧的launchWhenX方法使用自定义调度程序暂停协程,这可能会导致资源浪费,例如当活动被放到后台时。

正确的替代方案是要么取消执行,要么让它继续运行直到生命周期被销毁。如果你需要运行必须完成的作业,你应该在更一般的范围内开始你的执行,例如在你的viewModelScopeGlobalScope(小心处理!)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接