为什么我在 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。异步查询——查询 LiveDataFlowable 的 return 个实例——不受此规则约束,因为它们在需要时异步 运行 在后台线程上查询。

选项 A 使用 viewModelScope.launch。根据 the documentationviewModelScope 的默认调度程序是 Dispatchers.Main.immediate。由于 add 不是挂起方法,它直接在该调度程序上运行 - 即,它在主线程上运行。

选项 B 使用 viewModelScope.launch(Dispatchers.IO),这意味着代码在 IO 调度程序上运行。由于这不是主线程,所以它成功了。

选项C使add成为暂停功能。根据 Async queries with Kotlin coroutines guide,这会自动为您将数据库访问从主线程中移出,无论您使用的是什么调度程序。 选项 C 始终是使用 Room + Coroutines 时使用的正确技术