'Cannot access database on the main thread since it may potentially lock the UI for a long period of time' 另外,我使用协程访问了房间
'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
最初我直接访问了一个 viewModel 函数,该函数为我 运行 的查询启动了一个 viewModel 范围协程。那有同样的错误。
然后,我将 viewModel 函数更改为挂起函数,并从片段中的协程调用它。那也不管用。
所以,我这样做是为了在协程中调用该函数,然后 运行 viewModel 范围内的另一个协程。结果相同。
我认为在片段创建期间调用它可能负载太大。所以我尝试使用按钮 onclick 侦听器调用 viewModel 函数。又崩溃了。
我 运行 在数据库检查器中使用相同的查询,它运行良好。所以,查询也不是问题。
在下面的屏幕截图中,我包含了有关该问题的所有必要细节。只关注突出显示的内容。从传递列表片段(左上选项卡)开始。从那里,调用右上角选项卡中的 viewModel 函数。从那里 DAO 就在它下面。然后是它下面的数据class。
Android 工作室截图 -
viewModel 函数 -
fun resetAllAccess(){
viewModelScope.launch {
passwordDao.resetAccessForAll()
}
}
DAO 函数 -
@Query("UPDATE password_data SET access = 0 WHERE access = 1")
fun resetAccessForAll()
数据库的数据class -
@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() }
因为您使用的是viewModelScope
。很多人不知道这一点,但 viewModelScope
实际上被硬编码为使用主线程而不是另一个线程。
您可以在 Google official documentation 中找到此信息:
Note: The viewModelScope property of ViewModel classes is hardcoded to Dispatchers.Main. Replace it in tests by using Dispatchers.setMain with a TestCoroutineDispatcher as explained in the Easy coroutines in Android: viewModelScope blog post.
所以您可能想将协程范围传递到视图模型 class 构造函数(首选方式)或直接在视图模型中创建一个。那么你应该只用它来启动协程。
另一种常用的做法是创建一个同时具有视图模型和自定义范围(通过视图模型构造函数传递)的范围。
例如:
class ViewModelClass(customCoroutineScope: CoroutineScope): ViewModel {
private val backgroundScope: CoroutineContext = customCoroutineScope.coroutineContext + viewModelScope.coroutineContext
fun resetAllAccess(){
backgroundScope.launch {
passwordDao.resetAccessForAll()
}
}
}
您还可以找到更多信息 here 关于 viewModelScope
如何在后台工作的信息。
还有另一种选择,但我根本不推荐这种方法,原因很明显,那就是允许 Room 在主线程中使用 allowMainThreadQueries 进行查询 运行房间建造者。
我替换了这个函数-
fun resetAllAccess(){
viewModelScope.launch {
passwordDao.resetAccessForAll()
}
}
-与
fun resetAllAccess(){
CoroutineScope(Dispatchers.IO).launch {
passwordDao.resetAccessForAll()
}
}
所以现在,它不再是 运行 在主线程上了。
最初我直接访问了一个 viewModel 函数,该函数为我 运行 的查询启动了一个 viewModel 范围协程。那有同样的错误。
然后,我将 viewModel 函数更改为挂起函数,并从片段中的协程调用它。那也不管用。
所以,我这样做是为了在协程中调用该函数,然后 运行 viewModel 范围内的另一个协程。结果相同。
我认为在片段创建期间调用它可能负载太大。所以我尝试使用按钮 onclick 侦听器调用 viewModel 函数。又崩溃了。
我 运行 在数据库检查器中使用相同的查询,它运行良好。所以,查询也不是问题。
在下面的屏幕截图中,我包含了有关该问题的所有必要细节。只关注突出显示的内容。从传递列表片段(左上选项卡)开始。从那里,调用右上角选项卡中的 viewModel 函数。从那里 DAO 就在它下面。然后是它下面的数据class。
Android 工作室截图 -
viewModel 函数 -
fun resetAllAccess(){
viewModelScope.launch {
passwordDao.resetAccessForAll()
}
}
DAO 函数 -
@Query("UPDATE password_data SET access = 0 WHERE access = 1")
fun resetAccessForAll()
数据库的数据class -
@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() }
因为您使用的是viewModelScope
。很多人不知道这一点,但 viewModelScope
实际上被硬编码为使用主线程而不是另一个线程。
您可以在 Google official documentation 中找到此信息:
Note: The viewModelScope property of ViewModel classes is hardcoded to Dispatchers.Main. Replace it in tests by using Dispatchers.setMain with a TestCoroutineDispatcher as explained in the Easy coroutines in Android: viewModelScope blog post.
所以您可能想将协程范围传递到视图模型 class 构造函数(首选方式)或直接在视图模型中创建一个。那么你应该只用它来启动协程。
另一种常用的做法是创建一个同时具有视图模型和自定义范围(通过视图模型构造函数传递)的范围。
例如:
class ViewModelClass(customCoroutineScope: CoroutineScope): ViewModel {
private val backgroundScope: CoroutineContext = customCoroutineScope.coroutineContext + viewModelScope.coroutineContext
fun resetAllAccess(){
backgroundScope.launch {
passwordDao.resetAccessForAll()
}
}
}
您还可以找到更多信息 here 关于 viewModelScope
如何在后台工作的信息。
还有另一种选择,但我根本不推荐这种方法,原因很明显,那就是允许 Room 在主线程中使用 allowMainThreadQueries 进行查询 运行房间建造者。
我替换了这个函数-
fun resetAllAccess(){
viewModelScope.launch {
passwordDao.resetAccessForAll()
}
}
-与
fun resetAllAccess(){
CoroutineScope(Dispatchers.IO).launch {
passwordDao.resetAccessForAll()
}
}
所以现在,它不再是 运行 在主线程上了。