You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 15:33:20