web-dev-qa-db-fra.com

Salle - L'observateur LiveData ne se déclenche pas lorsque la base de données est mise à jour

J'essaie de découvrir dans le code ci-dessous, pourquoi est-ce que l'observable LiveData de Room ne me donne pas de nouveaux changements une fois que j'ai rempli la base de données avec de nouvelles données.

Ceci est mis sur la méthode onCreate de mon activité:

shiftsViewModel = ViewModelProviders.of(this).get(ShiftsViewModel.class);
shiftsViewModel
            .getShifts()
            .observe(this, this::populateAdapter);

Voici la méthode populateAdapter:

private void populateAdapter(@NonNull final List<Shift> shifts){

    recyclerView.setAdapter(new SimpleItemRecyclerViewAdapter(shifts));
}

J'ai également le code suivant qui remplit la base de données (j'utilise RxJava pour faire le travail sur un thread IO puisque Room a besoin que son code soit appelé en dehors du thread principal):

@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());
}

Le problème que je rencontre se produit lorsque j'appelle persistShifts après avoir commencé à observer mes shiftsViewModel. Je m'attendrais à ce que mon observateur (LiveData) soit déclenché avec tous les changements nouvellement ajoutés. Il s'avère que l'observateur est déclenché, mais une liste vide de changements est renvoyée à la place. La seule façon de le faire "fonctionner" est de quitter l'activité (donc de détruire le ViewModel actuel) et de recommencer. Cette fois, le LiveData de viewModel me donne tous les changements précédemment persistés, comme prévu.

Voici le reste du code:

@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);
}

ViewModel:

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;
   }
}

Pourquoi ne reçois-je pas immédiatement la liste des changements que je persiste dans le rappel observe ()?

20
Tiago

J'ai eu un problème similaire en utilisant Dagger 2 qui était dû à différentes instances du Dao, une pour la mise à jour/insertion de données et une autre instance fournissant les LiveData pour l'observation. Une fois que j'ai configuré Dagger pour gérer une instance singleton du Dao, je pourrais insérer des données en arrière-plan (dans mon cas dans un service) tout en observant LiveData dans mon activité - et le rappel onChange () serait appelé.

Cela revient à l'instance du Dao doit être la même instance qui insère/mise à jour des données et fournit LiveData pour l'observation.

42
mukwux

Dans mon cas, c'était parce que j'utilisais un MediatorLiveData pour convertir les entités renvoyées par la base de données et que j'avais oublié d'appeler setValue() avec le résultat converti, donc le médiateur ne faisait que relier les requêtes à la base de données mais ne notifiait jamais les résultats .

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)
    }
}
0
guy.gc