首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Kotlin结构化并发性[coroutine]模型的作用域是否适用于DB写入的UI?

Kotlin结构化并发性[coroutine]模型的作用域是否适用于DB写入的UI?
EN

Stack Overflow用户
提问于 2018-11-16 03:02:12
回答 1查看 252关注 0票数 2

我特别关注如何将用户发起的数据插入本地数据库。

以下模式在使用Kotlin与Architecture组件ViewModels的示例(包括官方来源,例如JetBrains、Google/ Android )中非常普遍。

代码语言:javascript
复制
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所有者关闭时会出现一些奇怪的时间问题,从而导致数据丢失?

我不想在不经意间引入一个计时错误,因为我误解了一些东西,并因此将我的代码转换为类似于上面所示的模型。

编辑:

所以,我决定做一个小小的测试,在我看来,使用这个模型向数据库写入的所有例子都可能有一个基本的错误。

修改代码以记录发生的情况,如下所示:

代码语言:javascript
复制
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")
    }
}

结果是,如果让它运行到完成:

代码语言:javascript
复制
D/thisIsCalledFromTheUI: Start
D/GlobalScope: Start
D/withContext: Start
D/withContext: End
D/thisIsCalledFromTheUI: End
D/GlobalScope: End

但是,如果在withContext结束之前关闭片段/活动(而不是应用程序),您将得到以下结果:

代码语言:javascript
复制
D/thisIsCalledFromTheUI: Start
D/GlobalScope: Start
D/withContext: Start
D/GlobalScope: End

至少对我来说,这表明你不能用它来向数据库写入非瞬态数据。

EN

回答 1

Stack Overflow用户

发布于 2018-11-16 09:39:43

我对文档的理解是,在上面的示例中,在UI上下文中启动的协同线(通过coroutineContext定义)将在ViewModel被销毁时被取消,但是withContext(Dispatchers.IO)块中的代码将运行到完成。

这不是对文档的正确阅读。withContext不启动另一个协同线,它只是在其块的持续时间内更改当前协同线的上下文。因此,这个协同机制将被取消,以及您启动的所有其他协同机制,而不会提供一个新的父上下文,该上下文与之关联的作业不同(或者根本没有作业,比如GlobalScope)。

但是,您建议的将GlobalScope用于持久操作的想法只是您正在测试的场景的本地修补程序,您仍然没有得到它将运行到完成的保证。用户可以完全退出应用程序,而Android可以终止该进程。

因此,如果您的目标是构建一个真正健壮的应用程序,那么您必须考虑到这样一个事实:在协同线完成之前,没有向DB写入任何信息。希望您在DB事务中运行该操作,如果程序被杀死,该操作将自动回滚,否则将不可能防止不一致。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53330816

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档