【问题标题】:viewModelScope not cancelledviewModelScope 未取消
【发布时间】:2019-09-25 13:20:36
【问题描述】:

看完Sean's explanation on Android (Google I/O'19)我也试过了:

init{
    viewModelScope.launch {
        Timber.i("coroutine awake")
        while (true){
            delay(2_000)
            Timber.i("another round trip")
        }
    }
}

不幸的是 onCleared 它在 Activity 被终止时被调用,但在它被置于后台时不会被调用(“当我们离开 Activity 时......”,背景是“离开”恕我直言^^)。
我得到以下输出:

> ---- Activity in Foreground
> 12:41:10.195  TEST: coroutine awake
> 12:41:12.215  TEST: another round trip
> 12:41:14.231  TEST: another round trip
> 12:41:16.245  TEST: another round trip
> 12:41:18.259  TEST: another round trip
> 12:41:20.270  TEST: another round trip
> ----- Activity in Background (on onCleared not fired)
> 12:41:22.283  TEST: another round trip
> 12:41:24.303  TEST: another round trip
> 12:41:26.320  TEST: another round trip
> 12:41:28.353  TEST: another round trip
> 12:41:30.361  TEST: another round trip
> ----- Activity in Foreground
> 12:41:30.369  TEST: coroutine awake

我该如何解决这个问题?

1 - 将代码从init 移动到suspend fun start()lifecycleScope.launchWhenStarted 内的活动调用?

我得到相同的结果。我以为lifecycleScope 在进入后台时会取消它的子协程,但我用这种方法得到了相同的 Timber 输出。

2 - 将我的 ViewModel 代码更改为:

private lateinit var job: Job

suspend fun startEmitting() {
    job = viewModelScope.launch {
        Timber.i("coroutine awake")
        while (true){
            delay(2_000)
            Timber.i("another round trip")
        }
    }
}
fun cancelJob(){
    if(job.isActive){
        job.cancel()
    }
}

而且,在我的活动中:

override fun onResume() {
    super.onResume()
    lifecycleScope.launch {
        viewModel.startEmitting()
    }
}
override fun onPause() {
    super.onPause()
    viewModel.cancelJob()
}

它可以工作,但viewModelScope 的目的不是为我管理CoroutineScope 吗?我讨厌这种 cancelJob 逻辑。

解决这个问题的最佳方法是什么?

【问题讨论】:

  • ViewModel 不知道 Activity 的生命周期,它只会对被销毁的 Activity 做出反应。您可以将您的逻辑移动到 LiveData 对象,然后您将能够对 onActive onInactive 回调做出反应。
  • 您的意思是使用 liveData{} ktx?因为实际上这个协程每个循环都会调用_someLiveData.value = ...。问题是,如果我将应用程序发送到后台,循环将继续。当然 UI 不会对此做出反应,但我们不应该避免循环的额外计算吗?

标签: android kotlin android-lifecycle kotlin-coroutines android-viewmodel


【解决方案1】:

Kotlin 无法为您取消无限操作。您需要在某处致电isActive。例如:while(isActive)

【讨论】:

  • 那是缺失的部分!奥布里加多·何塞 :)
  • 我发现了一些奇怪的事情:如果我每隔 2 秒就有一次前台活动,我会得到一个日志。如果我触摸主页/选项按钮,应用程序会进入后台,它仍然每 2 秒记录一次,大约 3/4 次。之后,它会在没有准确时间的情况下随机记录。例如:它可以在 20s、2s、6s 后登录。有什么特别的原因吗?我以为isActive 总是会在后台返回 false。
【解决方案2】:

您可以编写自己的LiveData 类,而不是使用MutableLiveData

当您这样做时,您可以覆盖在有任何活动侦听器时显式通知您的方法。

class MyViewModel : ViewModel(){
    val liveData = CustomLiveData(viewModelScope)

    class CustomLiveData(val scope) : LiveData<Int>(){
        private counter = 0
        private lateinit var job: Job

        override fun onActive() {
            job = scope.launch {
                Timber.i("coroutine awake")
                while (true){
                    delay(2_000)
                    Timber.i("another round trip")
                    postValue(counter++)
                }
        }

        override fun onInactive() {
            job.cancel()
        }
    }
}

然后在您的Activity 中,您不再需要显式调用任何启动/暂停,因为LiveData 将根据观察者状态自动开始和取消协程。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-05-20
    • 2020-01-04
    • 1970-01-01
    • 1970-01-01
    • 2022-06-29
    • 2019-10-11
    • 1970-01-01
    • 2022-10-24
    相关资源
    最近更新 更多