我特别关注如何将用户发起的数据插入本地数据库。
以下模式在使用Kotlin与Architecture组件ViewModels的示例(包括官方来源,例如JetBrains、Google/ Android )中非常普遍。
class CoroutineScopedViewModel : ViewModel(), CoroutineScope {
private val _job = Job()
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + _job
override fun onCleared() {
super.onCleared()
_job.cancel()
}
fun thisIsCalledFromTheUI() = launch {
/* do some UI stuff on the main thread */
withContext(Dispatchers.IO) {
try {
/* do some IO, e.g. inserting into DB */
} catch (error: IOException) {
/* do some exception handling */
}
}
}
}我对文档的理解是,在上面的示例中,在UI上下文中启动的协同线(通过coroutineContext定义)将在ViewModel被销毁时被取消,但是withContext(Dispatchers.IO)块中的代码将运行到完成。
但是,在我开始从(pre-1.0.0)全局范围内(启动/异步)协同模型重构我的项目之前,我觉得我需要澄清一些事情:
我对文件的阅读正确吗?或者,在withContext(Dispatchers.IO)块运行到完成之前,视图模型的破坏是否也会触发作业的取消?例如,该模型是否可以用于将数据插入到我的DB中,或者在用户回击或以其他方式导致ViewModel所有者关闭时会出现一些奇怪的时间问题,从而导致数据丢失?
我不想在不经意间引入一个计时错误,因为我误解了一些东西,并因此将我的代码转换为类似于上面所示的模型。
编辑:
所以,我决定做一个小小的测试,在我看来,使用这个模型向数据库写入的所有例子都可能有一个基本的错误。
修改代码以记录发生的情况,如下所示:
class ChildViewModel : ViewModel(), CoroutineScope {
private val _job = Job()
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + _job
override fun onCleared() {
super.onCleared()
Log.d("onCleared", "Start")
_job.cancel()
Log.d("onCleared", "End")
}
fun thisIsCalledFromTheUI() = launch {
Log.d("thisIsCalledFromTheUI", "Start")
GlobalScope.launch(Dispatchers.IO) {
Log.d("GlobalScope", "Start")
delay(15000)
Log.d("GlobalScope", "End")
}
withContext(Dispatchers.IO) {
Log.d("withContext", "Start")
delay(10000)
Log.d("withContext", "End")
}
Log.d("thisIsCalledFromTheUI", "End")
}
}结果是,如果让它运行到完成:
D/thisIsCalledFromTheUI: Start
D/GlobalScope: Start
D/withContext: Start
D/withContext: End
D/thisIsCalledFromTheUI: End
D/GlobalScope: End但是,如果在withContext结束之前关闭片段/活动(而不是应用程序),您将得到以下结果:
D/thisIsCalledFromTheUI: Start
D/GlobalScope: Start
D/withContext: Start
D/GlobalScope: End至少对我来说,这表明你不能用它来向数据库写入非瞬态数据。
发布于 2018-11-16 09:39:43
我对文档的理解是,在上面的示例中,在UI上下文中启动的协同线(通过
coroutineContext定义)将在ViewModel被销毁时被取消,但是withContext(Dispatchers.IO)块中的代码将运行到完成。
这不是对文档的正确阅读。withContext不启动另一个协同线,它只是在其块的持续时间内更改当前协同线的上下文。因此,这个协同机制将被取消,以及您启动的所有其他协同机制,而不会提供一个新的父上下文,该上下文与之关联的作业不同(或者根本没有作业,比如GlobalScope)。
但是,您建议的将GlobalScope用于持久操作的想法只是您正在测试的场景的本地修补程序,您仍然没有得到它将运行到完成的保证。用户可以完全退出应用程序,而Android可以终止该进程。
因此,如果您的目标是构建一个真正健壮的应用程序,那么您必须考虑到这样一个事实:在协同线完成之前,没有向DB写入任何信息。希望您在DB事务中运行该操作,如果程序被杀死,该操作将自动回滚,否则将不可能防止不一致。
https://stackoverflow.com/questions/53330816
复制相似问题