android - AndroidViewModel 按需取消作业
问题描述
我在AndroidViewModel
班里有一份工作。作业由 触发viewModelScope.launch
。Job 是一个长时间运行的进程,它通过 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
}
您将手动设置取消标志的位置,如果您设置它但没有在代码中检查它,它将永远不会停止
作为旁注,要小心,因为现在你有一大块计算正在运行代码,这将在一个线程上运行,因为你从未调用过挂起函数。当您添加 yield() 调用时,它可能会更改线程或上下文(在您定义的 ofc 范围内),因此请确保它是线程安全的
推荐阅读
- github - 错误:没有这样的文件或目录将 env 变量传递到 Github 工作流操作中的工作目录
- jquery - 2分钟后清除本地存储
- native - 如何使 ExceptionMapper 在本机模式下工作
- java - 在 addOnItemTouchListener 中禁用 recyclerView 原生广告的 onItemClick
- angular - 如何在 html 中的表单组上关闭 angular 8 Forms 中的自动完成功能?
- python - AttributeError:“NoneType”对象没有属性“drop”
- hyperlink - 如何在 Alchemy CMS 的内部页面链接上使用 relative_url_root
- python - python3中的Pickle vs cPickle(?)
- c++ - 想从我的字符串 c++ 中删除 \n\t 空格
- javascript - React Native,创建多个复选框