はじめに
以前にこちらの記事で、ライブクエリ、つまりデータベースの変更を監視するクエリについて、概要を記しました。
今回は、以下の公式チュートリアルの内容をベースに、Androidアプリ開発におけるライブクエリの利用について、具体的なコードを交えて解説します。
Couchbase Liteについては、Couchbase Mobileアプリケーション開発へのロードマップに記事をまとめている他、(これらの記事を元に構成した)以下の電子書籍を無償で頒布しています。
また、Couchbase Serverの存在意義、機能詳細、利用方法等については、拙著NoSQLドキュメント指向データベースCouchbase Serverファーストステップガイド(インプレスR&D刊)や、NoSQL/JSONデータベースCouchbase Server理解・活用へのロードマップにまとめてある記事をご参考ください。
Androidアプリ開発におけるライブクエリ利用
RecyclerView
RecyclerViewウィジェットは、Androidプラットフォームにおいて、動的なデータを効率的に表示するための選択肢です。
構成
Couchbase LiteをRecyclerViewのデータソースとして使用する際の一般的な構成を以下に示します。
以下、構成要素について、説明します。
-
RecyclerView
はActivity
によってインスタンス化されます。RecyclerView
はAdapter
と関係を持っています。 -
Adapter
は、ViewHolderパターンを使用してデータ項目をビューにバインドする役割を果たします。 - Couchbase Liteは、ビューに表示されるデータのデータソースになります。
-
DatabaseManager
は、CouchbaseLiteの初期化と管理を担当します。 - ライブクエリを構成するリスナーを、アプリは、クエリの結果に影響を与えるデータベースへの変更を受け取るリスナーを登録します。
シーケンスは以下の通りです
-
Activity
は、Couchbase Liteライブクエリを使用してデータベース内のアイテムをクエリし、クエリデータの更新を処理するためにリスナーを登録します。 - データベースでクエリ結果に影響を与えるデータの変更が行われるたびに、リスナーコールバックを介して
Activity
に変更が通知されます。 - アダプターに変更が通知されます。
- RecyclerViewは、更新されたデータセットで更新されます。
チュートリアル紹介
インストール
GitHubからUniversityListerプロジェクトのマスターブランチのクローンを作成します。ターミナルで次のコマンドを入力します
$ git clone https://github.com/couchbaselabs/UniversityLister-Android.git
アプリ概要
「UniversityLister」アプリは、データソースとしての「University」のリストをRecyclerview
を使って表示します。
「University」エントリがCouchbase Liteデータベースに追加されるたびに、RecyclerViewが自動的に更新され、新しく追加されたアイテムが表示されます。
コードによる解説
チュートリアルで利用されているコードについて、開設します。ここでのコードの抜粋は、ライブクエリの解説としてみた場合、冗長な部分もありますが、JSONドキュメントとオブジェクトとの変換についてなど、Couchbase Liteを用いたアプリ開発の例として、参考としてもらえれば幸いです。
初期化
ListActivity.javaファイルを開き、onCreate
メソッドを確認します。ここですべての初期化が行われます。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Couchbase Liteデータベースマネージャーの初期化
dbMgr = new DatabaseManager(this);
// 典型的なコンテンツレイアウトの初期化
setContentView(R.layout.activity_list);
// ツールバー
Toolbar toolbar = (Toolbar) findViewById(R.id.university_toolbar);
setSupportActionBar(toolbar);
// RecyclerViewの設定
RecyclerView recyclerView = (RecyclerView)findViewById(R.id.rvUniversities);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
// ローカルサンプルファイルからデータを非同期にロードするためのAsynchronously Load the data from local sample file
DataFetcher fetcher = new DataFetcher(this,this);
fetcher.execute();
}
-
DatabaseManager
は、Couchbase Liteのインスタンスの作成/オープンを担当します。 -
DataFetcher
はAsyncTask
であり、アプリにバンドルされているファイルからサンプルデータを読み込む役割を果たします。これは、データの外部ソースをシミュレートするものです。AsyncTask
のexecute()
メソッドを呼び出します。これについては、次のセクションで詳しく説明します。
サンプルデータ読み込み
DataFetcher.javaファイルを開き、doInBackground()
メソッドを確認します。アプリの起動中、DataFetcher
クラスはアプリにバンドルされているローカルファイルからサンプルのデータをロードします。データのロードは、AsyncTask
を使用してバックグラウンドスレッドで実行されます。
@Override
protected List<University> doInBackground(Void... voids) {
// サンプルファイルパス(コンテンツはJSON形式)
String fileName = "university_sample.txt";
StringBuilder stringBuilder = new StringBuilder();
List<University> universities = null;
try {
// assetsフォルダーのローカルサンプルデータファイルからデータをロード
InputStream inputStream = mContext.getAssets().open(fileName);
// Jacksonライブラリを使って、JSONからUniversity POJOのリストにマップ
ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
universities = Arrays.asList(mapper.readValue(inputStream, University[].class));
return universities;
} catch (IOException e ) {
e.printStackTrace();
return null;
}
}
@Override
protected void onPostExecute(List<University> result) {
// IDataFetchResponseデレゲート(ここではListActivity)へデータロード完了を通知
mDelegate.postResult(result);
}
この時点では、サンプルデータ(sampleData
)はCouchbase Liteデータベースに保存されていません。このサンプルデータがどのように使用されるかについては、チュートリアルの後半で説明します。
ライブクエリ
ListActivity.javaファイルを開き、QueryForListOfUniversities()
メソッドを確認します。このアクティビティでは、Couchbase Liteデータベースから大学のリストを取得するための「ライブクエリ」を設定します。
private void QueryForListOfUniversities() {
try {
// データベースから全てのドキュメントをフェッチするクエリの構築
query = QueryBuilder.
select(SelectResult.all()).
from(DataSource.database(dbMgr.database));
// ライブクエリリスナーを追加
query.addChangeListener(new QueryChangeListener() {
@Override
public void changed(QueryChange change) {
ResultSet resultRows = change.getResults();
Result row;
List<University> universities = new ArrayList<University>();
// リスナーコールバック内における、結果セットの反復処理
while ((row = resultRows.next()) != null) {
// Jacksonライブラリの`ObjectMapper`を使用
ObjectMapper objectMapper = new ObjectMapper();
// 宣言されていないプロパティを無視する
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// エントリに対応する`ReadOnlyDictionary`オブジェクトを取得
Dictionary valueMap = row.getDictionary(dbMgr.database.getName());
// `ReadOnlyDictionary`タイプから`University`POJOに変換
University university = objectMapper.convertValue(valueMap.toMap(),University.class);
universities.add(university);
}
// Update the adapter with the newly added University documents
adapter.setUniversities(universities);
runOnUiThread(new Runnable() {
@Override
public void run() {
// 変更されたドキュメントでアダプターへ通知
adapter.notifyDataSetChanged();
}
});
}
}
);
// リスナーの登録されたクエリの実行
query.execute();
}
catch (IllegalArgumentException e) {
} catch (CouchbaseLiteException e) {
e.printStackTrace();
}
}
データベースの更新
ListActivity.javaファイルを開き、fetchUniversityAndAddToDatabase()
メソッドを確認します。このfetchUniversityAndAddToDatabase()
メソッドでは、サンプルデータのデータ項目をCouchbase Liteに挿入します。
ここではライブクエリに関わる通知などの処理と直接関係していないことにご留意ください。
private void fetchUniversityAndAddToDatabase() {
Random r = new Random();
int index = r.nextInt(sampleData.size()-1);
try {
// ランダムに選択されたインデックスでUniversityオブジェクトを選択
University university = sampleData.get(index);
// オブジェクトとドキュメントの変換を実施するマッパー
ObjectMapper objectMapper = new ObjectMapper();
// 宣言されていないプロパティを無視する
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
HashMap<String,Object> universityMap = objectMapper.convertValue(university,HashMap.class);
MutableDocument doc = new MutableDocument(universityMap);
// ドキュメントをデータベースに保存する
dbMgr.database.save(doc);
}
catch ( CouchbaseLiteException | NullPointerException e) {
e.printStackTrace();
}
}
最後に
Couchbase LiteデータベースをAndroidアプリ内のRecyclerViews
の埋め込みデータソースとして使用する方法の例を紹介しました。