如何让Room的LiveData观察者仅在指定列变更时触发?
问题根源
Room的LiveData查询是基于整张表的变更监听,不管你的查询语句有没有涉及被修改的列,只要目标表(TABLE_NAME)发生任何写入操作(比如修改收藏列),Room都会判定表数据有变化,进而触发对应的LiveData观察者回调。这是Room的设计逻辑,它不会追踪具体哪几列被修改,只关注表是否有更新动作。
另外注意你Fragment里的mViewModel.updateSingle( mAdapter.getNumber(0));,如果这个方法是对同一张表执行写入操作,会进一步触发LiveData的更新循环,加重问题。
解决办法
1. 让distinctUntilChanged真正生效
你已经用了Transformations.distinctUntilChanged(mData),但默认的比较是引用比较,对于List<Entity_Random>,必须自定义内容比较逻辑,确保只有当列表里的实体内容(你关心的3列)真的变化时,才通知观察者:
public LiveData<List<Entity_Random>> getData() { return Transformations.distinctUntilChanged(mData, (oldList, newList) -> { if (oldList.size() != newList.size()) return false; for (int i = 0; i < oldList.size(); i++) { Entity_Random oldItem = oldList.get(i); Entity_Random newItem = newList.get(i); // 只比较你查询的三个字段:num、title、time return oldItem.getNum().equals(newItem.getNum()) && oldItem.getTitle().equals(newItem.getTitle()) && oldItem.getTime().equals(newItem.getTime()); } return true; }); }
这样即使Room触发了更新,只要返回的列表内容和上次一致,就不会通知UI刷新。
2. 去掉不必要的表写入操作
检查updateSingle方法,如果它的作用只是获取单条数据,别用写入操作,改成查询:
// DAO里新增查询方法 @Query("SELECT " + COLUMN_NUM + ", " + COLUMN_TITLE + ", " + COLUMN_TIME + " FROM " + TABLE_NAME + " WHERE " + COLUMN_NUM + " = :num") LiveData<Entity_Random> getSingleHymn(int num); // ViewModel里替换updateSingle为查询方法 public LiveData<Entity_Random> getSingleHymn(int num) { return dao.getSingleHymn(num); }
然后在Fragment里观察这个LiveData,而不是执行写入,避免额外触发表变更。
3. 自定义过滤LiveData(进阶)
如果上面的方法还不够,可以自己实现一个LiveData,手动过滤掉无效更新:
public class ContentFilteredLiveData extends MediatorLiveData<List<Entity_Random>> { private List<Entity_Random> mLastValidData; public ContentFilteredLiveData(LiveData<List<Entity_Random>> source) { addSource(source, newData -> { if (mLastValidData == null || !isContentEqual(mLastValidData, newData)) { mLastValidData = newData; setValue(newData); } }); } private boolean isContentEqual(List<Entity_Random> oldList, List<Entity_Random> newList) { if (oldList.size() != newList.size()) return false; for (int i = 0; i < oldList.size(); i++) { Entity_Random oldItem = oldList.get(i); Entity_Random newItem = newList.get(i); if (!oldItem.getNum().equals(newItem.getNum()) || !oldItem.getTitle().equals(newItem.getTitle()) || !oldItem.getTime().equals(newItem.getTime())) { return false; } } return true; } }
在ViewModel里使用:
public LiveData<List<Entity_Random>> getData() { return new ContentFilteredLiveData(mData); }
总结
本质是Room的表级监听特性导致的,解决核心就是过滤掉内容无变化的更新,同时避免不必要的表写入操作,减少触发更新的源头。
内容的提问来源于stack exchange,提问作者Marco Jacobo
相关产品推荐
相关产品推荐

