LiveData.getValue() returns null with Room

52,689

Solution 1

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

Solution 2

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.

Solution 3

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

Solution 4

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.

Solution 5

I would suggest to create another query without LiveData if you need to synchronously fetch data from database in your 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();
    }
}
Share:
52,689
S Haque
Author by

S Haque

I am an Android Developer with more than 5 years of experience in this field; passionate about developing better applications through writing performance-optimized, reusable, and testable code. My core strengths lie within my profound knowledge on Algorithm, Data-Structure, Software Architecture and Software Design Patterns which helps me to write robust and scalable applications. I also love to learn about the latest practices and library components of the continuously evolving Android world. Furthermore, I am a user-centric developer. I love to travel and explore new places and I also love sports. Recently, I am emphasizing a lot on self-improvement, building good habits like reading, meditation and exercising to improve my productivity.

Updated on June 09, 2020

Comments

  • S Haque
    S Haque about 4 years

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