如何使用房间执行繁重的数据库操作?

Posted

技术标签:

【中文标题】如何使用房间执行繁重的数据库操作?【英文标题】:How to perform heavy database operations with room? 【发布时间】:2020-04-19 19:20:18 【问题描述】:

我正在使用房间数据库来运行一些繁重的数据库操作。我没有将 LiveData 用于此操作,因为我仅将结果用于计算。现在如果在我的主要片段中我有这个

override fun onActivityCreated(savedInstanceState: Bundle?) 
    lifecycleScope.launch 
        val result = viewModel.someHeavyOperation() // a suspend fun
        doSomething(result)
    

我在启动时收到Skipped xx frames! Your application might be doing to much work on main thread.,如果我忽略数据库查询,我不会收到。

现在这里的一些答案,如 this one 或 that one 似乎建议在 IO 线程上运行查询,如

override fun onActivityCreated(savedInstanceState: Bundle?) 
    lifecycleScope.launch 
        withContext(Dispatchers.IO) 
            val result = viewModel.someHeavyOperation() // a suspend fun
        
        doSomething(result)
    

这确实提高了我的表现。让我感到困惑的是,android 开发者在媒体上发布的 an article 中写道

注意:Room 使用自己的调度程序在后台线程上运行查询。您的代码不应使用 withContext(Dispatchers.IO) 来调用暂停房间查询。它会使代码复杂化并使您的查询运行速度变慢。

但是,他们似乎只考虑了昂贵的位是随后的计算的情况,他们似乎提出了类似的建议

override fun onActivityCreated(savedInstanceState: Bundle?) 
    lifecycleScope.launch 
        val result = viewModel.someOperation() // a suspend fun
        withContext(Dispatchers.Default) 
            doSomethingHeavy(result)
        
    

现在我的问题是:

    如果房间仍然使用自定义调度程序,那么调用哪个调度程序房间查询又有什么关系? 如何在不阻塞主线程的情况下执行昂贵的房间查询?

【问题讨论】:

【参考方案1】:

如果房间仍然使用自定义调度程序,那么调用哪个调度程序房间查询又有什么关系?

Room 从2.1 版本开始引入了Kotlin Coroutine 支持。早些时候,他们不支持Coroutine。所以,首先,从build.gradle文件确定你的房间版本是否为2.1或以上:

implementation "androidx.room:room-coroutines:$versions.room"

如果您使用的Room 版本早于2.1,则Room 将在调用者线程上执行操作。这意味着如果我们在 MAIN 线程上对 Room 进行查询调用,它将在 MAIN 上执行操作。如果我们在 IO - background 线程上调用 Room,它将在 background 上执行。

如何在不阻塞主线程的情况下执行昂贵的房间查询?

为此,我们应该在 IO 线程上调用 Room 查询。你已经在做正确的事情了

override fun onActivityCreated(savedInstanceState: Bundle?) 
    lifecycleScope.launch 
        withContext(Dispatchers.IO) 
            val result = viewModel.someHeavyOperation() // a suspend fun
        
        doSomething(result)
    

除此之外,如果需要添加等待Room查询调用返回,可以使用asynclauncher和await()方法

override fun onActivityCreated(savedInstanceState: Bundle?) 
    lifecycleScope.launch 
    val content = async(Dispatchers.IO) 
        viewModel.someHeavyOperation() // a suspend fun
    
    // Using below line, we are introducing waiting for completion of someHeavyOperation() on IO thread
    // If we return any result from someHeavyOperation(), it can be accessed in result variable as below
    var result = content.await() 
    
   

参考:

    https://***.com/a/59376666/1994950 https://***.com/a/59408634/1994950

【讨论】:

那么为什么medium article 说我们不应该从 IO 线程调用房间查询? 我在我的问题中引用了它:“注意:房间使用自己的调度程序在后台线程上运行查询。您的代码不应使用 withContext(Dispatchers.IO) 来调用暂停房间查询。它将使代码复杂化并使查询运行速度变慢。” 好的,正如我所提到的,在 Room 版本 2.1 之后,建议不要使用开发者方面的 IO 线程,因为 Room 正在为我们做这件事。但是,如果我们的遗留代码使用的是2.1以下的Room版本,则需要开发者处理。 这是建议,而不是规则。我们仍然可以使用IO 作为2.1 版本的Room 的良好实践 感谢您澄清这一点。我使用版本2.2.3,但我的代码使用Dispatchers.IO 运行得更快。我认为这是因为 ViewModel/Repository 代码中引入的复杂性...

以上是关于如何使用房间执行繁重的数据库操作?的主要内容,如果未能解决你的问题,请参考以下文章

房间持久性库。删除所有

如何在 C++ 代码中的一些“繁重”操作之前使 QML 对象可见

在 c# 中用于繁重 IO 操作的线程类型

关闭浏览器时如何在队列中执行非常繁重的任务(在后台运行)?

棋牌游戏服务器如何动态创建房间?

删除房间内数据库