Objet Java POJO
public class Section {
@ColumnInfo(name="section_id")
public int mSectionId;
@ColumnInfo(name="section_name")
public String mSectionName;
public int getSectionId() {
return mSectionId;
}
public void setSectionId(int mSectionId) {
this.mSectionId = mSectionId;
}
public String getSectionName() {
return mSectionName;
}
public void setSectionName(String mSectionName) {
this.mSectionName = mSectionName;
}
}
Ma méthode de requête
@Query("SELECT * FROM section")
LiveData<List<Section>> getAllSections();
Accéder à la base de données
final LiveData<List<Section>> sections = mDb.sectionDAO().getAllSections();
Sur la ligne suivante, je vérifie la fonction sections.getValue()
qui me donne toujours la valeur null bien que des données se trouvent dans la base de données et que, plus tard, je reçois la valeur dans la méthode onChanged()
.
sections.observe(this, new Observer<List<Section>>() {
@Override
public void onChanged(@Nullable List<Section> sections){
}
});
Mais lorsque j'omets LiveData de la requête, je reçois les données comme prévu. Méthode de requête:
@Query("SELECT * FROM section")
List<Section> getAllSections();
Accéder à la base de données:
final List<Section> sections = mDb.sectionDAO().getAllSections();
Je résous ce problème par cette approche
private MediatorLiveData<List<Section>> mSectionLive = new MediatorLiveData<>();
.
.
.
@Override
public LiveData<List<Section>> getAllSections() {
final LiveData<List<Section>> sections = mDb.sectionDAO().getAllSections();
mSectionLive.addSource(sections, new Observer<List<Section>>() {
@Override
public void onChanged(@Nullable List<Section> sectionList) {
if(sectionList == null || sectionList.isEmpty()) {
// Fetch data from API
}else{
mSectionLive.removeSource(sections);
mSectionLive.setValue(sectionList);
}
}
});
return mSectionLive;
}
Sur la ligne suivante, je vérifie sections.getValue (), qui me donne toujours la valeur null bien que des données se trouvent dans la base de données et que plus tard, je reçois la valeur dans la méthode onChanged ().
Ce comportement est normal car les requêtes qui renvoient LiveData fonctionnent de manière asynchrone. La valeur est nulle à ce moment.
Donc appeler cette méthode
LiveData<List<Section>> getAllSections();
vous obtiendrez le résultat plus tard ici
sections.observe(this, new Observer<List<Section>>() {
@Override
public void onChanged(@Nullable List<Section> sections){
}
});
de la documentation:
Room n'autorise pas l'accès à la base de données sur le thread principal, sauf si vous avez appelé allowMainThreadQueries () sur le générateur, car il pourrait potentiellement verrouiller l'interface utilisateur pendant une longue période. Les requêtes asynchrones (les requêtes qui renvoient LiveData ou RxJava Flowable) sont exemptées de cette règle car elles exécutent la requête de manière asynchrone sur un thread en arrière-plan en cas de besoin.
si sections.getValue () est null, je dois appeler api for data et l'insérer dans la base de données
Vous pouvez gérer ceci à la méthode onChange
:
sections.observe(this, new Observer<List<Section>>() {
@Override
public void onChanged(@Nullable List<Section> sections){
if(sections == null || sections.size() == 0) {
// No data in your database, call your api for data
} else {
// One or more items retrieved, no need to call your api for data.
}
}
});
Mais vous devriez mieux placer cette logique d’initialisation Base de données/Table dans une classe de référentiel. Départ exemple de Google . Voir DatabaseCreator classe.
J'ai résolu le problème similaire comme suit
Dans votre classe ViewModel
private LiveData<List<Section>> mSections;
@Override
public LiveData<List<Section>> getAllSections() {
if (mSections == null) {
mSections = mDb.sectionDAO().getAllSections();
}
return mSections;
}
C'est tout requis. Ne changez jamais l'instance de LiveData.
LiveData est une requête asynchrone, vous obtenez l'objet LiveData mais il peut ne contenir aucune donnée. Vous pouvez utiliser une méthode supplémentaire pour attendre que les données soient renseignées, puis extraire les données.
public static <T> T getValue(LiveData<T> liveData) throws InterruptedException {
final Object[] objects = new Object[1];
final CountDownLatch latch = new CountDownLatch(1);
Observer observer = new Observer() {
@Override
public void onChanged(@Nullable Object o) {
objects[0] = o;
latch.countDown();
liveData.removeObserver(this);
}
};
liveData.observeForever(observer);
latch.await(2, TimeUnit.SECONDS);
return (T) objects[0];
}
Je suggérerais de créer une autre requête sans LiveData si vous devez extraire de manière synchrone des données de la base de données dans votre code.
DAO:
@Query("SELECT COUNT(*) FROM section")
int countAllSections();
ViewModel:
Integer countAllSections() {
return new CountAllSectionsTask().execute().get();
}
private static class CountAllSectionsTask extends AsyncTask<Void, Void, Integer> {
@Override
protected Integer doInBackground(Void... notes) {
return mDb.sectionDAO().countAllSections();
}
}