AndroidViewModel 按需取消作业

AndroidViewModel cancel job on demand

我在 AndroidViewModel class 有一份工作。作业由 viewModelScope.launch 触发。作业是一个很长的 运行 过程,它 return 由 lambda 函数产生。根据要求,如果用户想取消作业,同时保留在按钮的范围内,则单击它应该取消作业。问题是当我取消作业时,进程仍在后台 运行 并且它正在计算后台任务。下面是我的 ViewModelClass 及其作业和取消功能。

import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.*
class SelectionViewModel(val app: Application) : AndroidViewModel(app) {

private var mainJob: Job? = null
private var context: Context? = null

fun performAction(
    fileOptions: FileOptions,
    onSuccess: (ArrayList<String>?) -> Unit,
    onFailure: (String?) -> Unit,
    onProgress: (Pair<Int, Int>) -> Unit
) {
    mainJob = viewModelScope.launch {
        withContext(Dispatchers.IO) {
            kotlin.runCatching {

                while (isActive) {
                    val mOutputFilePaths: ArrayList<String> = ArrayList()
                    // Long running Background task
                     .. progress 
                      OnProgress.invoke(pair)

                     // resul success
                    onSuccess.invoke(mOutputFilePaths)

                }


            }.onFailure {
                withContext(Dispatchers.Main) {
                    onFailure.invoke(it.localizedMessage)
                }
            }
        }
    }

}

fun cancelJob() {
    mainJob?.cancel()
  }
}

这是我正在启动我的 ViewModel

 val viewModel: SelectionViewModelby lazy {
    ViewModelProviders.of(this).get(SelectionViewModel::class.java)
}

当我开始工作时,我调用以下方法

viewModel.performAction(fileOptions,{success->},{failure->},{progress->})

当我想取消任务时。我调用下面的方法。

viewModel.cancelJob()

问题是即使在取消作业后我仍然在调用它时收到进度。这意味着作业尚未取消。 我想在保留在视图模型范围内的同时实现启动和取消作业的正确方法。

那么实现视图模型启动和取消作业的正确方法是什么?

要取消作业,您必须调用暂停函数。

这意味着如果你的工作有这样的代码

while (canRead) {
   read()
   addResults()
}
return result

这永远无法按照您希望的方式取消。

您可以通过两种方式取消此代码

a) 添加延迟功能(这将检查取消并取消您的作业)

b)(在上面的例子中是正确的方法)定期添加一个 yield() 函数

所以上面的代码应该是这样的:

while(canRead) {
    yield()
    read()
    addResults()
}
return result

编辑:可能需要一些进一步的解释才能清楚地说明这一点

只是因为你 运行 有上下文,并不意味着协程可以随时停止或中断它

协程所做的基本上是改变用回调做事的旧方式,并用挂起函数代替它

我们过去对复杂计算所做的是启动一个线程,该线程将执行计算,然后得到一个包含结果的回调。

您可以随时取消线程,工作将停止。

取消协程不一样

如果您取消协程,您基本上要做的就是告诉它作业已取消,并且在下一个合适的时刻它应该停止

但如果你不使用 yield() delay() 或任何暂停函数,这样的时机永远不会到来

它相当于运行使用线程

宁这样的东西
while(canRead && !cancelled) {
   doStuff
}

手动设置取消标志的地方,如果你设置它但没有在你的代码中检查它,它永远不会停止

作为旁注,请小心,因为现在您有一大块计算 运行ning 代码,这将 运行 在一个线程上进行,因为您从未调用过挂起函数。当您添加 yield() 调用时,它可能会更改线程或上下文(在您定义的 ofc 内),因此请确保它是线程安全的