我发现Dao返回的LiveData会在DB中更新行时调用它的观察者,即使LiveData值显然没有改变。
考虑类似以下示例的情况:
示例实体
@Entity
public class User {
public long id;
public String name;
// example for other variables
public Date lastActiveDateTime;
}
example DAO
@Dao
public interface UserDao {
// I am only interested in the user name
@Query("SELECT name From User")
LiveData<List<String>> getAllNamesOfUser();
@Update(onConflict = OnConflictStrategy.REPLACE)
void updateUser(User user);
}
某个地方在后台线程
UserDao userDao = //.... getting the dao
User user = // obtain from dao....
user.lastActiveDateTime = new Date(); // no change to user.name
userDao.updateUser(user);
UI中的某个地方
// omitted ViewModel for simplicity
userDao.getAllNamesOfUser().observe(this, new Observer<List<String>> {
@Override
public void onChanged(@Nullable List<String> userNames) {
// this will be called whenever the background thread called updateUser.
// If user.name is not changed, it will be called with userNames
// with the same value again and again when lastActiveDateTime changed.
}
});
在此示例中,ui仅对用户名感兴趣,因此LiveData的查询仅包括名称字段。但是,即使只更新了其他字段,仍会在Dao Update上调用observer.onChanged。 (事实上,如果我没有对User实体进行任何更改并调用UserDao.updateUser,则仍会调用observer.onChanged)
这是Dao LiveData在室内设计的行为吗?我是否有机会解决这个问题,以便只在更新所选字段时才会调用观察者?
编辑:我更改为使用以下查询将lastActiveDateTime值更新为评论建议中的KuLdip PaTel。仍然会调用用户名LiveData的观察者。
@Query("UPDATE User set lastActiveDateTime = :lastActiveDateTime where id = :id")
void updateLastActiveDateTime(Date lastActiveDateTime, int id);
这种情况被称为观察者的误报通知。请检查link中提到的第7点以避免此类问题。
下面的例子是用kotlin编写的,但你可以使用它的java版本来实现它。
fun <T> LiveData<T>.getDistinct(): LiveData<T> {
val distinctLiveData = MediatorLiveData<T>()
distinctLiveData.addSource(this, object : Observer<T> {
private var initialized = false
private var lastObj: T? = null
override fun onChanged(obj: T?) {
if (!initialized) {
initialized = true
lastObj = obj
distinctLiveData.postValue(lastObj)
} else if ((obj == null && lastObj != null)
|| obj != lastObj) {
lastObj = obj
distinctLiveData.postValue(lastObj)
}
}
})
return distinctLiveData
}
转换方法distinctUntilChanged
中有一个简单的解决方案。只有在数据被更改时才会显示新数据。
在这种情况下,我们只在源数发生变化时获取数据:
LiveData<YourType> getData(){
return Transformations.distinctUntilChanged(LiveData<YourType> source));
}
但对于事件案例,最好使用它:https://stackoverflow.com/a/55212795/9381524
目前没有办法停止触发Observer.onChanged,这就是为什么我认为LiveData对于大多数使用某些连接的查询都没用。就像@Pinakin提到的那样,有一个MediatorLiveData,但这只是一个过滤器,每次更改时数据仍会被加载。想象一下,在1个查询中有3个左连接,其中只需要一个或两个字段来自这些连接。如果每次从这4个表(主+ 3个连接表)中的任何记录更新时实现PagedList,将再次调用该查询。对于一些具有少量数据的表来说这是可以的,但如果我错了,请纠正我,如果更大的表,这将是坏的。最好是我们只有在主表更新时才能设置要刷新的查询,或者理想情况下只有在数据库中更新该查询的字段时才能刷新。
我遇到了同样的问题。
我做错了什么:
1)创建匿名对象:
private LiveData<List<WordsTableEntity>> listLiveData;
// listLiveData = ... //init our LiveData...
listLiveData.observe(this, new Observer<List<WordsTableEntity>>() {
@Override
public void onChanged(@Nullable List<WordsTableEntity> wordsTableEntities) {
}
});
在我的例子中,我多次调用该方法所在的方法。
From the docs我认为,新观察者从LiveData获取数据。因此,如果他设置观察onChanged
,那么作者可以从少数新的无名观察者那里接受很少的userDao.getAllNamesOfUser().observe(this, new Observer
方法。
在LiveData.observe(...
和之前创建命名的Observer对象会更好
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
observer = new Observer<List<WordsTableEntity>>() {
@Override
public void onChanged(@Nullable List<WordsTableEntity> wordsTableEntities) {
adapter.setWordsTableEntities(wordsTableEntities);
progressBar.setVisibility(View.GONE);
}
};
}
然后设置它LiveData.observe(observer
,我们首次从LieData接收数据,然后,当数据将被更改时。
2)多次观察一个Observe对象
public void callMethodMultipleTimes(String searchText) {
listLiveData = App.getRepositoryRoomDB().searchDataExceptChapter(searchText);
listLiveData.observe(this, observer);
}
我多次调用这个方法,调试显示我,我正在添加我的observer
多次,因为我称为callMethodMultipleTimes();
我们的listLiveData
是一个全球变量,它的存在。它在这里更改对象引用
listLiveData = App.getRepositoryRoomDB().searchDataExceptChapter(searchText);
,但内存中的旧对象不会立即删除
如果我们之前打电话给listLiveData.removeObserver(observer);
,这将是固定的
listLiveData = App.getRepositoryRoomDB().searchDataExceptChapter(searchText);
并返回1) - 我们不能调用listLiveData.removeObserver(our anonimous Observer);
,因为我们没有匿名对象引用。
所以,在结果中我们可以这样做:
private Observer observer;
private LiveData<List<WordsTableEntity>> listLiveData;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
observer = new Observer<List<WordsTableEntity>>() {
@Override
public void onChanged(@Nullable List<WordsTableEntity> wordsTableEntities) {
adapter.setWordsTableEntities(wordsTableEntities);
progressBar.setVisibility(View.GONE);
}
};
}
public void searchText(String searchText) {
if (listLiveData != null){
listLiveData.removeObservers(this);
}
listLiveData = App.getRepositoryRoomDB().searchDataExceptChapter(searchText);
listLiveData.observe(this, observer);
}
我没有使用不同的功能。就我而言,它没有明显的作用。
我希望我的案子可以帮助别人。
附:库的版本
// Room components
implementation "android.arch.persistence.room:runtime:1.1.1"
annotationProcessor "android.arch.persistence.room:compiler:1.1.1"
androidTestImplementation "android.arch.persistence.room:testing:1.1.1"
// Lifecycle components
implementation "android.arch.lifecycle:extensions:1.1.1"
annotationProcessor "android.arch.lifecycle:compiler:1.1.1"