Kotlin Room Database sum查询结果存储及主线程报错解决
问题核心原因
崩溃日志已经给出明确根因:
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
你在主线程同步执行Room数据库查询操作,触发了Room默认的主线程访问保护机制,导致ViewModel初始化失败直接崩溃。
你当前的代码在Repository类初始化、ViewModel初始化阶段就同步调用了数据库查询方法,这些逻辑都运行在主线程,必然触发这个异常。
修复方案
按以下层级修改代码即可解决问题:
- 修改Dao层,将同步查询改为挂起函数,Room会自动对挂起函数做IO线程调度,不会阻塞主线程
// 类名遵循Kotlin大驼峰命名规范,不要用小写开头 data class SumPojo(var sumOvertime: Double) @Dao interface OvertimeDao { // 增加suspend修饰符,标记为挂起函数 @Query("SELECT SUM(overtime) as sumOvertime FROM TableOvertime") suspend fun getSumOvertime(): SumPojo }
- 修改Repository层,移除初始化时的同步查询逻辑,对外提供挂起查询方法
class OvertimeRepository(private val overtimeDao: OvertimeDao) { // 不要在类构造/初始化阶段直接调用数据库查询,改为挂起方法供上层调用 suspend fun getSumOvertime(): SumPojo { return overtimeDao.getSumOvertime() } }
- 修改ViewModel层,使用协程异步执行查询,用可观察数据容器持有结果,避免同步阻塞主线程
class OvertimeViewModel(application: Application): AndroidViewModel(application) { private val repository : OvertimeRepository // 用MutableLiveData承载查询结果,对外暴露不可变的LiveData供UI层观察 private val _sumOvertime = MutableLiveData<SumPojo>() val sumOvertime: LiveData<SumPojo> = _sumOvertime init { val overtimeDao = OvertimeDatabase.getDatabase(application).overtimeDao() repository = OvertimeRepository(overtimeDao) // 在viewModelScope中执行挂起查询,指定IO调度器在子线程跑数据库操作 viewModelScope.launch(Dispatchers.IO) { val result = repository.getSumOvertime() // 查询完成后切回主线程更新数据 _sumOvertime.postValue(result) } } }
- 修改Fragment层,不要同步获取查询结果,改为观察LiveData,数据返回后自动更新UI
// 类名遵循大驼峰命名规范 class InputWorktimeFragment : Fragment() { private lateinit var mOvertimeViewModel : OvertimeViewModel override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val view = inflater.inflate(R.layout.fragment_input_worktime, container, false) mOvertimeViewModel = ViewModelProvider(this)[OvertimeViewModel::class.java] // 观察数据变化,查询完成后会自动触发回调 mOvertimeViewModel.sumOvertime.observe(viewLifecycleOwner) { sumResult -> // 在此处获取sumResult.sumOvertime的值更新UI即可,比如赋值给TextView } return view } }
临时调试时可以在Room数据库构建代码中调用
allowMainThreadQueries()方法绕过主线程检查,但这个方法禁止在正式发布版本使用,会导致UI卡顿、ANR等问题,仅适合本地快速调试场景。
内容的提问来源于stack exchange,提问作者diniska
相关产品推荐
相关产品推荐

