web-dev-qa-db-fra.com

LiveData.getValue () renvoie null avec Room

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();
22
CodeCameo

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

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.

12
snersesyan

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.

2
Devrim

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.

2
Napolean

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];
}
1
Yanbin Hu

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