RoomDB读取全量消息时并发删除触发IllegalStateException崩溃如何解决?
异常触发原因
这个异常是SQLite并发读写未做隔离导致的Cursor数据失效,具体触发逻辑如下:
- 后台线程执行
getAllMessages全量查询时,SQLite首先生成对应Cursor,Cursor会记录当前message表的总行数、数据偏移等元信息,查询过程分批次从CursorWindow读取数据填充返回结果 - 此时用户触发的删除操作在另一个线程执行,删除了表中部分数据,数据库实际行数少于Cursor初始化时记录的行数
- 待查询线程读取到已被删除的行数据时(对应报错中的第1426行),CursorWindow中不存在对应位置的数据,直接抛出
IllegalStateException
修复方案
- 添加事务注解保证快照读:给全量查询方法加上
@Transaction注解,Room会将整个查询包裹在只读事务中,事务启动时会生成当前数据库的快照,后续的删除操作不会修改该快照的内容,查询全程读取的都是事务启动时的一致数据,不会出现Cursor失效问题。修改后的DAO代码如下:
@Transaction @Query("SELECT * FROM message") suspend fun getAllMessages(): List<Message>?
- 串行化数据库操作:所有数据库读写操作统一指定到同一个单线程调度器执行,避免同一时间存在并发的读写操作,从根源消除Cursor被并发修改的可能。
- 异常兜底重试:在数据访问层对查询操作增加
IllegalStateException捕获逻辑,捕获到Cursor相关异常时自动重试查询,避免应用直接崩溃。 - 优化大表查询逻辑:如果
message表数据量较大,将全量查询改为分页查询,减少单次查询的执行时长,降低读写冲突的发生概率。
内容的提问来源于stack exchange,提问作者Happy Mittal
相关产品推荐
相关产品推荐

