web-dev-qa-db-ja.com

LiveData.getValue()はRoomで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()をチェックしています。これは、データベースにデータがありますが、後でonChanged()メソッドで値を取得していますが、常にnullを返します。

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

    }
});
_

しかし、クエリからLiveDataを省略すると、期待どおりにデータが取得されます。クエリ方法:

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

DBへのアクセス:

_final List<Section> sections = mDb.sectionDAO().getAllSections();
_
22
CodeCameo

このアプローチでこの問題を解決します

    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

次の行では、sections.getValue()をチェックしています。これは、データベースにデータがありますが、後でonChanged()メソッドで値を取得していますが、常にnullを返します。

LiveDataを返すクエリは非同期で動作するため、これは通常の動作です。その瞬間、値はnullです。

したがって、このメソッドを呼び出す

LiveData<List<Section>> getAllSections();

ここで結果を取得します

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

}
});

ドキュメントから:

ルームは、ビルダーでallowMainThreadQueries()を呼び出した場合を除き、メインスレッド上のデータベースへのアクセスを許可しません。これは、UIが長時間ロックされる可能性があるためです。非同期クエリ(LiveDataまたはRxJava Flowableを返すクエリ)は、必要に応じてバックグラウンドスレッドで非同期にクエリを実行するため、このルールから除外されます。

12
snersesyan

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 クラスを参照してください。

2
Devrim

同様の問題を次のように解決しました

ViewModelクラス内

private LiveData<List<Section>> mSections;

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

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

    return mSections;
}

これはすべて必須です。 LiveDataのインスタンスを変更しないでください。

2
Napolean

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

コード内のデータベースからデータを同期的に取得する必要がある場合は、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();
    }
}
0
MrBinWin