Kotlin协程distinctUntilChanged配合Room数据库使用异常怎么解决?
问题根因
- 意外修改了旧数据的引用实例
你当前的Story实体所有字段都是var可变类型,如果你在更新数据时直接修改了从Flow返回的列表中的Story实例属性,再将其存入数据库,会直接修改distinctUntilChanged内部缓存的旧列表里对应实例的属性值。等数据库变更触发Flow返回新列表时,旧数据的isSeen已经被你提前改成了true,自然比对时新旧值完全一致,无参distinctUntilChanged依赖的equals方法也会返回true,无法触发更新。 - 自定义比对逻辑存在越界BUG
你写的循环for (i in 0..new.size)使用了闭区间运算符,会遍历到等于new.size的下标,而列表的最大下标为new.size - 1,运行到最后一位会抛出IndexOutOfBoundsException,导致比对逻辑异常终止,Flow无法正常下发更新。
修复步骤
- 修改实例时使用copy生成新对象
更新Story属性时,禁止直接修改原实例,调用data class的copy方法生成新实例后再修改属性:
// 错误写法:直接修改原引用的属性 val targetStory = list[index] targetStory.isSeen = true update(targetStory) // 正确写法:生成新实例修改,不影响旧缓存 val updatedStory = list[index].copy(isSeen = true) update(updatedStory)
如果没有特殊需求,建议将Story实体的所有字段改为val不可变类型,从根源避免意外修改原引用的问题。
2. 修复比对逻辑的边界错误
将循环改为左闭右开区间:
storiesDao.getStories(today).distinctUntilChanged { old, new -> if (new.size != old.size) return@distinctUntilChanged false // 用until替代..,避免越界 for (i in 0 until new.size) { if (old[i].isSeen != new[i].isSeen) return@distinctUntilChanged false } return@distinctUntilChanged true }.collect { ... }
如果需要全字段比对,直接使用无参distinctUntilChanged()即可,不需要自定义比对逻辑,前提是确认StoryItem是data class、Date类型的equals方法符合预期。
3. 可选优化:避免使用GlobalScope
改用业务生命周期绑定的协程Scope执行数据库操作,避免内存泄漏:
// 示例:在ViewModel中使用viewModelScope fun update(story: Story) { viewModelScope.launch(Dispatchers.IO) { storiesDao.updateStories(story) } }
内容的提问来源于stack exchange,提问作者Man
相关产品推荐
相关产品推荐

