LiveData.getValue() возвращает null с комнатой

Объект 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;
    }
}

Мой метод запроса

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

Доступ к DB

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

В следующей строке я проверяю sections.getValue(), который всегда дает мне null, хотя у меня есть данные в DataBase, а позже я получаю значение в методе onChanged().

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

    }
});

Но когда я опускаю LiveData из запроса, я получаю данные, как ожидалось. Метод запроса:

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

Доступ к БД:

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

Ответ 1

Я решаю эту проблему с помощью этого подхода

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

Ответ 2

  На следующей строке я проверяю section.getValue(), которая всегда дает мне нулевое значение, хотя у меня есть данные в базе данных, а позже я получаю значение в методе onChanged().

Это нормальное поведение, потому что запросы, которые возвращают LiveData, работают асинхронно. В этот момент значение равно нулю.

Так что вызывая этот метод

LiveData<List<Section>> getAllSections();

вы получите результат позже здесь

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

}
});

из документации:

Room не разрешает доступ к базе данных в главном потоке, если вы не вызвали allowMainThreadQueries() в компоновщике, поскольку это может потенциально блокировать пользовательский интерфейс на длительные периоды времени. Асинхронные запросы (запросы, которые возвращают LiveData или RxJava Flowable) исключаются из этого правила, поскольку при необходимости они асинхронно запускают запрос в фоновом потоке.

Ответ 3

if sections.getValue() null Я должен вызвать api для данных и вставить в базу данных

Вы можете справиться с этим методом 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.
         }
    }
});

Но лучше поместить эту логику инициализации базы данных/таблицы в класс репозитория. Проверьте образец Google. См. DatabaseCreator класс.

Ответ 4

Я разрешил аналогичную проблему следующим образом

Внутри класса ViewModel

private LiveData<List<Section>> mSections;

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

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

    return mSections;
}

Это все необходимо. Никогда не изменяйте экземпляр LiveData.

Ответ 5

LiveData - это асинхронный запрос, вы получаете объект LiveData, но он может не содержать данных. Вы можете использовать дополнительный метод, чтобы дождаться заполнения данных и затем извлечь данные.

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

Ответ 6

Я бы предложил создать другой запрос без LiveData, если вам нужно синхронно получать данные из базы данных в вашем коде.

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