viewModelScope 未取消
viewModelScope not cancelled
看完后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 被杀死时被调用,但当它被置于后台时不会被调用("when we move away from the Activity...",背景是 "moving away" 恕我直言 ^^)。
我得到以下输出:
> ---- 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
移动到 lifecycleScope.launchWhenStarted
中的 activity 调用的 suspend fun start()
?
我得到了同样的结果。我以为 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()
}
}
而且,在我的 Activity 中:
override fun onResume() {
super.onResume()
lifecycleScope.launch {
viewModel.startEmitting()
}
}
override fun onPause() {
super.onPause()
viewModel.cancelJob()
}
很好用,但 viewModelScope
的目的不就是为我管理 CoroutineScope
吗?我讨厌这种 cancelJob 逻辑。
处理此问题的最佳方法是什么?
Kotlin 无法为您取消无限操作。您需要在某处调用 isActive
。例如:while(isActive)
.
您可以编写自己的 LiveData
class 而不是使用 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
中,您不再需要显式调用任何 start/pause,因为 LiveData
将根据观察者状态自动开始和取消协程。
看完后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 被杀死时被调用,但当它被置于后台时不会被调用("when we move away from the Activity...",背景是 "moving away" 恕我直言 ^^)。
我得到以下输出:
> ---- 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
移动到 lifecycleScope.launchWhenStarted
中的 activity 调用的 suspend fun start()
?
我得到了同样的结果。我以为 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()
}
}
而且,在我的 Activity 中:
override fun onResume() {
super.onResume()
lifecycleScope.launch {
viewModel.startEmitting()
}
}
override fun onPause() {
super.onPause()
viewModel.cancelJob()
}
很好用,但 viewModelScope
的目的不就是为我管理 CoroutineScope
吗?我讨厌这种 cancelJob 逻辑。
处理此问题的最佳方法是什么?
Kotlin 无法为您取消无限操作。您需要在某处调用 isActive
。例如:while(isActive)
.
您可以编写自己的 LiveData
class 而不是使用 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
中,您不再需要显式调用任何 start/pause,因为 LiveData
将根据观察者状态自动开始和取消协程。