Room - LiveData 观察器在更新数据库时不会触发
Room - LiveData observer does not trigger when database is updated
我试图在下面的代码中找出,为什么在我用新数据填充数据库后 Room 的 LiveData observable 没有给我新的班次。
这是我的 activity 的 onCreate 方法:
shiftsViewModel = ViewModelProviders.of(this).get(ShiftsViewModel.class);
shiftsViewModel
.getShifts()
.observe(this, this::populateAdapter);
这是 populateAdapter 方法:
private void populateAdapter(@NonNull final List<Shift> shifts){
recyclerView.setAdapter(new SimpleItemRecyclerViewAdapter(shifts));
}
我还有以下填充数据库的代码(我使用 RxJava 在 IO 线程上完成工作,因为 Room 需要在主线程之外调用它的代码):
@Override
public Observable<List<Shift>> persistShifts(@NonNull final List<Shift> shifts){
return Observable.fromCallable(() -> {
appDatabase.getShiftDao().insertAll(shifts);
return shifts;
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
我在开始观察 shiftsViewModel 后调用 persistShifts 时出现问题。我希望我的观察者 (LiveData) 会被所有新添加的班次触发。结果是触发了观察者,但返回了一个空的班次列表。使其成为 "work" 的唯一方法是如果我离开 activity (因此破坏当前的 ViewModel)并再次进入。这次 viewModel 的 LiveData 为我提供了之前保留的所有变化,正如预期的那样。
这是代码的其余部分:
@Entity
public class Shift{
@PrimaryKey
private long id;
private String start;
private String end;
private String startLatitude;
private String startLongitude;
private String endLatitude;
private String endLongitude;
private String image;
...
DAO:
@Dao
public interface ShiftDAO {
@Query("SELECT * FROM shift")
LiveData<List<Shift>> getAll();
@Query("SELECT * FROM shift WHERE id = :id")
LiveData<Shift> getShiftById(long id);
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertAll(List<Shift> shifts);
}
视图模型:
public class ShiftsViewModel extends AndroidViewModel{
private final ISQLDatabase sqlDatabase;
private MutableLiveData<Shift> currentShift;
private LiveData<List<Shift>> shifts;
private boolean firstTimeCreated;
public ShiftsViewModel(final Application application){
super(application);
this.sqlDatabase = ((ThisApplication) application).getSQLDatabase();
this.firstTimeCreated = true;
}
public MutableLiveData<Shift> getCurrentlySelectedShift(){
if(currentShift == null){
currentShift = new MutableLiveData<>();
}
return currentShift;
}
public LiveData<List<Shift>> getShifts() {
if(shifts == null){
shifts = sqlDatabase.queryAllShifts();
}
return shifts;
}
public void setCurrentlySelectedShift(final Shift shift){
currentShift = getCurrentlySelectedShift();
currentShift.setValue(shift);
}
public boolean isFirstTimeCreated(){
return firstTimeCreated;
}
public void alreadyUsed(){
firstTimeCreated = false;
}
}
为什么我直接在 observe() 回调中没有得到我坚持的班次列表?
我在使用 Dagger 2 时遇到了类似的问题,这是由于 Dao 的实例不同,一个用于 updating/inserting 数据,另一个实例提供用于观察的 LiveData。一旦我将 Dagger 配置为管理 Dao 的单例实例,然后我可以在后台插入数据(在我的例子中是在服务中),同时在我的 Activity 中观察 LiveData - 并且将调用 onChange() 回调。
归结为Dao的实例必须是inserting/updating数据和提供LiveData观察的同一个实例。
在我的例子中,这是因为我使用 MediatorLiveData 来转换从数据库返回的实体,而忘记用转换后的结果调用 setValue()
,所以中介只依赖于对数据库的请求,但是从不通知结果。
override fun getItems() = MediatorLiveData<List<Item>>().apply {
addSource(itemDao().getItems()) {
// I was previously only converting the items, without calling 'value ='
value = it.map(ItemWithTags::toDto)
}
}
我试图在下面的代码中找出,为什么在我用新数据填充数据库后 Room 的 LiveData observable 没有给我新的班次。
这是我的 activity 的 onCreate 方法:
shiftsViewModel = ViewModelProviders.of(this).get(ShiftsViewModel.class);
shiftsViewModel
.getShifts()
.observe(this, this::populateAdapter);
这是 populateAdapter 方法:
private void populateAdapter(@NonNull final List<Shift> shifts){
recyclerView.setAdapter(new SimpleItemRecyclerViewAdapter(shifts));
}
我还有以下填充数据库的代码(我使用 RxJava 在 IO 线程上完成工作,因为 Room 需要在主线程之外调用它的代码):
@Override
public Observable<List<Shift>> persistShifts(@NonNull final List<Shift> shifts){
return Observable.fromCallable(() -> {
appDatabase.getShiftDao().insertAll(shifts);
return shifts;
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
我在开始观察 shiftsViewModel 后调用 persistShifts 时出现问题。我希望我的观察者 (LiveData) 会被所有新添加的班次触发。结果是触发了观察者,但返回了一个空的班次列表。使其成为 "work" 的唯一方法是如果我离开 activity (因此破坏当前的 ViewModel)并再次进入。这次 viewModel 的 LiveData 为我提供了之前保留的所有变化,正如预期的那样。
这是代码的其余部分:
@Entity
public class Shift{
@PrimaryKey
private long id;
private String start;
private String end;
private String startLatitude;
private String startLongitude;
private String endLatitude;
private String endLongitude;
private String image;
...
DAO:
@Dao
public interface ShiftDAO {
@Query("SELECT * FROM shift")
LiveData<List<Shift>> getAll();
@Query("SELECT * FROM shift WHERE id = :id")
LiveData<Shift> getShiftById(long id);
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertAll(List<Shift> shifts);
}
视图模型:
public class ShiftsViewModel extends AndroidViewModel{
private final ISQLDatabase sqlDatabase;
private MutableLiveData<Shift> currentShift;
private LiveData<List<Shift>> shifts;
private boolean firstTimeCreated;
public ShiftsViewModel(final Application application){
super(application);
this.sqlDatabase = ((ThisApplication) application).getSQLDatabase();
this.firstTimeCreated = true;
}
public MutableLiveData<Shift> getCurrentlySelectedShift(){
if(currentShift == null){
currentShift = new MutableLiveData<>();
}
return currentShift;
}
public LiveData<List<Shift>> getShifts() {
if(shifts == null){
shifts = sqlDatabase.queryAllShifts();
}
return shifts;
}
public void setCurrentlySelectedShift(final Shift shift){
currentShift = getCurrentlySelectedShift();
currentShift.setValue(shift);
}
public boolean isFirstTimeCreated(){
return firstTimeCreated;
}
public void alreadyUsed(){
firstTimeCreated = false;
}
}
为什么我直接在 observe() 回调中没有得到我坚持的班次列表?
我在使用 Dagger 2 时遇到了类似的问题,这是由于 Dao 的实例不同,一个用于 updating/inserting 数据,另一个实例提供用于观察的 LiveData。一旦我将 Dagger 配置为管理 Dao 的单例实例,然后我可以在后台插入数据(在我的例子中是在服务中),同时在我的 Activity 中观察 LiveData - 并且将调用 onChange() 回调。
归结为Dao的实例必须是inserting/updating数据和提供LiveData观察的同一个实例。
在我的例子中,这是因为我使用 MediatorLiveData 来转换从数据库返回的实体,而忘记用转换后的结果调用 setValue()
,所以中介只依赖于对数据库的请求,但是从不通知结果。
override fun getItems() = MediatorLiveData<List<Item>>().apply {
addSource(itemDao().getItems()) {
// I was previously only converting the items, without calling 'value ='
value = it.map(ItemWithTags::toDto)
}
}