Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LiveData.getValue() returns null with Room

Java POJO Object

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

My Query method

@Query("SELECT * FROM section")
LiveData<List<Section>> getAllSections();

Accessing DB

final LiveData<List<Section>> sections = mDb.sectionDAO().getAllSections();

On the next line I am checking sections.getValue() which is always giving me null although I have data in the DataBase and later I am getting the value in the onChanged() method.

sections.observe(this, new Observer<List<Section>>() {
    @Override
    public void onChanged(@Nullable List<Section> sections){

    }
});

But when I omit LiveData from the query I am getting the data as expected. Query Method:

@Query("SELECT * FROM section")
List<Section> getAllSections();

Accessing DB:

final List<Section> sections = mDb.sectionDAO().getAllSections();
like image 724
S Haque Avatar asked Jun 08 '17 06:06

S Haque


People also ask

Why is LiveData null?

getValue() , while livedata are used to observe the data. May be when you are checking in next line, data is not set in livedata that's why it gives you null. In short if you don't want to use LiveData then just use it without LiveData .

Can LiveData be null?

LiveData is written in Java. It does allow setting it's value to null .

How can data be stored in LiveData in a ViewModel?

Use LiveData for the app's data (word, word count and the score) in the Unscramble app. Add observer methods that get notified when the data changes, update the scrambled word text view automatically. Write binding expressions in the layout file, which are triggered when the underlying LiveData is changed.


4 Answers

On the next line I am checking sections.getValue() which is always giving me null although I have data in the DataBase and later I am getting the value in the onChanged() method.

This is a normal behavior, because queries that return LiveData, are working asynchronous. The value is null that moment.

So calling this method

LiveData<List<Section>> getAllSections(); 

you will get the result later here

sections.observe(this, new Observer<List<Section>>() { @Override public void onChanged(@Nullable List<Section> sections){  } }); 

from documentation:

Room does not allow accessing the database on the main thread unless you called allowMainThreadQueries() on the builder because it might potentially lock the UI for a long periods of time. Asynchronous queries (queries that return LiveData or RxJava Flowable) are exempt from this rule since they asynchronously run the query on a background thread when needed.

like image 50
snersesyan Avatar answered Oct 11 '22 03:10

snersesyan


I solve this problem through this approach

    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;     } 
like image 23
S Haque Avatar answered Oct 11 '22 04:10

S Haque


LiveData is an asynchronous query, you get the LiveData object but it might contain no data. You could use an extra method to wait for the data to be filled and then extract the data.

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];
}
like image 20
Yanbin Hu Avatar answered Oct 11 '22 03:10

Yanbin Hu


I resolved the similar issue as follows

Inside your ViewModel class

private LiveData<List<Section>> mSections;

@Override
public LiveData<List<Section>> getAllSections() {

    if (mSections == null) {
        mSections = mDb.sectionDAO().getAllSections();
    }

    return mSections;
}

This is all required. Never change the LiveData's instance.

like image 27
Napolean Avatar answered Oct 11 '22 02:10

Napolean