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();
_
このアプローチでこの問題を解決します
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;
}
次の行では、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を返すクエリ)は、必要に応じてバックグラウンドスレッドで非同期にクエリを実行するため、このルールから除外されます。
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 クラスを参照してください。
同様の問題を次のように解決しました
ViewModelクラス内
private LiveData<List<Section>> mSections;
@Override
public LiveData<List<Section>> getAllSections() {
if (mSections == null) {
mSections = mDb.sectionDAO().getAllSections();
}
return mSections;
}
これはすべて必須です。 LiveDataのインスタンスを変更しないでください。
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];
}
コード内のデータベースからデータを同期的に取得する必要がある場合は、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();
}
}