为什么我在 Android Studio 中出现 'Cannot access database on the main thread since it may potentially lock the UI for a long period of time.' 错误?
Why do I get 'Cannot access database on the main thread since it may potentially lock the UI for a long period of time.' error in Android Studio?
我在 运行 代码 A 时收到错误 "java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time."
,为什么?
代码B用fun add(aMVoice: MVoice)=viewModelScope.launch (Dispatchers.IO){}
替换后可以正常使用,为什么?
代码C用suspend fun add(aMVoice: MVoice
替换后可以正常使用,为什么?
还有更多,Code B和Code C哪个更好?
代码A
@Dao
interface DBVoiceDao{
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun add(aMVoice: MVoice)
}
class DBVoiceRepository private constructor(private val mDBVoiceDao: DBVoiceDao){
suspend fun add(aMVoice: MVoice){
mDBVoiceDao.add(aMVoice)
}
}
class HomeViewModel(private val mDBVoiceRepository: DBVoiceRepository) : ViewModel() {
fun add(aMVoice: MVoice)=viewModelScope.launch{
mDBVoiceRepository.add(aMVoice)
}
}
class FragmentHome : Fragment() {
private val mHomeViewModel by lazy {...}
...
btnInsert.setOnClickListener {
val aMVoice = MVoice()
mHomeViewModel.add(aMVoice)
}
...
}
代码B
@Dao
interface DBVoiceDao{
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun add(aMVoice: MVoice)
}
class DBVoiceRepository private constructor(private val mDBVoiceDao: DBVoiceDao){
suspend fun add(aMVoice: MVoice){
mDBVoiceDao.add(aMVoice)
}
}
class HomeViewModel(private val mDBVoiceRepository: DBVoiceRepository) : ViewModel() {
fun add(aMVoice: MVoice)=viewModelScope.launch (Dispatchers.IO){ //I add Dispatchers.IO
mDBVoiceRepository.add(aMVoice)
}
}
class FragmentHome : Fragment() {
private val mHomeViewModel by lazy {...}
...
btnInsert.setOnClickListener {
val aMVoice = MVoice()
mHomeViewModel.add(aMVoice)
}
...
}
代码C
@Dao
interface DBVoiceDao{
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun add(aMVoice: MVoice) //I add suspend
}
class DBVoiceRepository private constructor(private val mDBVoiceDao: DBVoiceDao){
suspend fun add(aMVoice: MVoice){
mDBVoiceDao.add(aMVoice)
}
}
class HomeViewModel(private val mDBVoiceRepository: DBVoiceRepository) : ViewModel() {
fun add(aMVoice: MVoice)=viewModelScope.launch {
mDBVoiceRepository.add(aMVoice)
}
}
class FragmentHome : Fragment() {
private val mHomeViewModel by lazy {...}
...
btnInsert.setOnClickListener {
val aMVoice = MVoice()
mHomeViewModel.add(aMVoice)
}
...
}
}
Room 不支持在主线程上访问数据库,除非您在构建器上调用了 allowMainThreadQueries()
,因为它可能会长时间锁定 UI。异步查询——查询 LiveData
或 Flowable
的 return 个实例——不受此规则约束,因为它们在需要时异步 运行 在后台线程上查询。
选项 A 使用 viewModelScope.launch
。根据 the documentation,viewModelScope
的默认调度程序是 Dispatchers.Main.immediate
。由于 add
不是挂起方法,它直接在该调度程序上运行 - 即,它在主线程上运行。
选项 B 使用 viewModelScope.launch(Dispatchers.IO)
,这意味着代码在 IO 调度程序上运行。由于这不是主线程,所以它成功了。
选项C使add
成为暂停功能。根据 Async queries with Kotlin coroutines guide,这会自动为您将数据库访问从主线程中移出,无论您使用的是什么调度程序。 选项 C 始终是使用 Room + Coroutines 时使用的正确技术
我在 运行 代码 A 时收到错误 "java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time."
,为什么?
代码B用fun add(aMVoice: MVoice)=viewModelScope.launch (Dispatchers.IO){}
替换后可以正常使用,为什么?
代码C用suspend fun add(aMVoice: MVoice
替换后可以正常使用,为什么?
还有更多,Code B和Code C哪个更好?
代码A
@Dao
interface DBVoiceDao{
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun add(aMVoice: MVoice)
}
class DBVoiceRepository private constructor(private val mDBVoiceDao: DBVoiceDao){
suspend fun add(aMVoice: MVoice){
mDBVoiceDao.add(aMVoice)
}
}
class HomeViewModel(private val mDBVoiceRepository: DBVoiceRepository) : ViewModel() {
fun add(aMVoice: MVoice)=viewModelScope.launch{
mDBVoiceRepository.add(aMVoice)
}
}
class FragmentHome : Fragment() {
private val mHomeViewModel by lazy {...}
...
btnInsert.setOnClickListener {
val aMVoice = MVoice()
mHomeViewModel.add(aMVoice)
}
...
}
代码B
@Dao
interface DBVoiceDao{
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun add(aMVoice: MVoice)
}
class DBVoiceRepository private constructor(private val mDBVoiceDao: DBVoiceDao){
suspend fun add(aMVoice: MVoice){
mDBVoiceDao.add(aMVoice)
}
}
class HomeViewModel(private val mDBVoiceRepository: DBVoiceRepository) : ViewModel() {
fun add(aMVoice: MVoice)=viewModelScope.launch (Dispatchers.IO){ //I add Dispatchers.IO
mDBVoiceRepository.add(aMVoice)
}
}
class FragmentHome : Fragment() {
private val mHomeViewModel by lazy {...}
...
btnInsert.setOnClickListener {
val aMVoice = MVoice()
mHomeViewModel.add(aMVoice)
}
...
}
代码C
@Dao
interface DBVoiceDao{
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun add(aMVoice: MVoice) //I add suspend
}
class DBVoiceRepository private constructor(private val mDBVoiceDao: DBVoiceDao){
suspend fun add(aMVoice: MVoice){
mDBVoiceDao.add(aMVoice)
}
}
class HomeViewModel(private val mDBVoiceRepository: DBVoiceRepository) : ViewModel() {
fun add(aMVoice: MVoice)=viewModelScope.launch {
mDBVoiceRepository.add(aMVoice)
}
}
class FragmentHome : Fragment() {
private val mHomeViewModel by lazy {...}
...
btnInsert.setOnClickListener {
val aMVoice = MVoice()
mHomeViewModel.add(aMVoice)
}
...
}
}
Room 不支持在主线程上访问数据库,除非您在构建器上调用了 allowMainThreadQueries()
,因为它可能会长时间锁定 UI。异步查询——查询 LiveData
或 Flowable
的 return 个实例——不受此规则约束,因为它们在需要时异步 运行 在后台线程上查询。
选项 A 使用 viewModelScope.launch
。根据 the documentation,viewModelScope
的默认调度程序是 Dispatchers.Main.immediate
。由于 add
不是挂起方法,它直接在该调度程序上运行 - 即,它在主线程上运行。
选项 B 使用 viewModelScope.launch(Dispatchers.IO)
,这意味着代码在 IO 调度程序上运行。由于这不是主线程,所以它成功了。
选项C使add
成为暂停功能。根据 Async queries with Kotlin coroutines guide,这会自动为您将数据库访问从主线程中移出,无论您使用的是什么调度程序。 选项 C 始终是使用 Room + Coroutines 时使用的正确技术