【问题标题】:'Cannot access database on the main thread since it may potentially lock the UI for a long period of time' Also, I Accessed room using coroutine'无法访问主线程上的数据库,因为它可能会长时间锁定 UI' 另外,我使用协程访问了房间
【发布时间】:2022-01-03 14:14:53
【问题描述】:

最初,我直接访问了一个 viewModel 函数,该函数为我正在运行的查询启动了一个 viewModel 范围协程。那有同样的错误。

然后,我将 viewModel 函数更改为挂起函数,并从片段中的协程调用它。那也没用。

所以,我使函数在协程中被调用,然后在 viewModel 范围内运行另一个协程。这给出了相同的结果。

我认为在片段创建过程中调用它可能负载太大。所以我尝试使用按钮 onclick 侦听器调用 viewModel 函数。又崩溃了。

我在数据库检查器中运行了相同的查询,它运行良好。所以,查询也不是问题。

在下面的屏幕截图中,我包含了有关该问题的所有必要细节。只关注突出显示的内容。从传递列表片段(左上选项卡)开始。从那里,调用右上角选项卡中的 viewModel 函数。从那里开始,它正下方的 DAO。然后是它下面的数据类。

Android 工作室截图 -

viewModel 函数 -

fun resetAllAccess(){
    viewModelScope.launch {
        passwordDao.resetAccessForAll()
    }
}

DAO 函数 -

@Query("UPDATE password_data SET access = 0 WHERE access = 1")
    fun resetAccessForAll()

数据库的数据类-

@Entity(tableName = "password_data")
data class PasswordData(
    @PrimaryKey(autoGenerate = true) val id: Int = 0,
    @ColumnInfo(name = "service_name") var serviceName: String,
    @ColumnInfo(name = "service_password") var servicePassword: String,
    @ColumnInfo(name = "is_an_application") var isAnApplication: Boolean = false,
    @ColumnInfo(name = "use_finger_print") var useFingerPrint: Boolean = true,
    @ColumnInfo(name = "access") var access: Boolean = false
)

从片段发出的调用 -

CoroutineScope(Dispatchers.IO).launch { viewModel.resetAllAccess() }

【问题讨论】:

  • 尝试在resetAccessForAll上添加suspend
  • 已经试过了。同样的错误。
  • 请粘贴您的实际代码而不是您的代码图片。然后我们可以复制粘贴并显示固定版本。
  • 是的。就这么做了。

标签: android sqlite kotlin android-room


【解决方案1】:

这是因为您使用的是viewModelScope。很多人不知道这一点,但 viewModelScope 实际上是硬编码的,以使用主线程而不是另一个线程。

你可以在Google official documentation找到这个信息:

注意:ViewModel 类的 viewModelScope 属性被硬编码为 Dispatchers.Main。使用 Dispatchers.setMain 和 TestCoroutineDispatcher 在测试中替换它,如 Android 中的 Easy coroutines: viewModelScope 博客文章中所述。

因此,您可能希望将协程范围传递给视图模型类构造函数(首选方式)或直接在视图模型中创建一个。然后你应该使用它来启动协程。

另一种常用的做法是创建具有视图模型和自定义范围的范围(通过视图模型构造函数传递)。

例如:

class ViewModelClass(customCoroutineScope: CoroutineScope): ViewModel {

    private val backgroundScope: CoroutineContext = customCoroutineScope.coroutineContext + viewModelScope.coroutineContext
    

    fun resetAllAccess(){
        backgroundScope.launch {
            passwordDao.resetAccessForAll()
        }
    }
}

您还可以找到更多关于 here 的信息,了解 viewModelScope 如何在后台工作。

还有另一种选择,但我根本不推荐这样做,原因很明显,那就是允许 Room 在主线程中运行查询,方法是在 Room 构建器中使用 allowMainThreadQueries。

【讨论】:

    【解决方案2】:

    我替换了这个函数 -

    fun resetAllAccess(){
        viewModelScope.launch {
            passwordDao.resetAccessForAll()
        }
    }
    

    -与

    fun resetAllAccess(){
            CoroutineScope(Dispatchers.IO).launch {
                passwordDao.resetAccessForAll()
            }
        }
    

    所以现在,它不再在主线程上运行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-12-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-01
      相关资源
      最近更新 更多