FURYU Tech Blog - フリュー株式会社

フリュー株式会社の開発者が技術情報を発信するブログです。

SQLBriteの紹介

コンテンツ・メディア第1事業部の荒木です。ピクトリンクというプリントシール画像を使ったアプリのAndroid版を開発しています。今回は、RxJava好きのAndroid開発者なら知っておきたいライブラリSQLBriteの紹介をしたいと思います。

SQLBriteとは?

https://github.com/square/sqlbrite

SQLBriteは、Android端末内のデータベース管理に使われるSQLiteOpenHelperをラップするライブラリです。最大の特徴は、テーブルに対するSQLクエリをRxJavaのObservableとして購読できることです。対象のテーブルに変更があるとSQL文が再度実行されて結果が流れてきます。これによって、テーブルのデータをストリームのように扱うことが可能になります。

 

使い方

ダウンロード

build.gradleのdependenciesに以下の行を追加してください。2016/09/15時点の最新バージョンは0.7.0です。

compile 'com.squareup.sqlbrite:sqlbrite:0.7.0'

BriteDatabaseの取得

まずは、SQLiteOpenHelperをラップしているBriteDatabaseを取得します。

SqlBrite sqlBrite = SqlBrite.create();
BriteDatabase db = sqlBrite.wrapDatabaseHelper(helper, Schedulers.io());

SQLBrite#wrapDatabaseHelper(helper, scheduler)メソッドでBriteDatabaseを取得できます。第1引数のhelperには、SQLiteOpenHelperを継承した自作クラス(ここにテーブルの作成やマイグレーション処理を書く)を入れます。第2引数にはテーブル変更の通知を行うスレッドを指定してください。通常はSchedulers.io()で良いと思います。

クエリの購読

クエリの購読には、BriteDatabase#createQuery()メソッドを利用します。

たとえば、usersテーブルの全てのレコードを取得するSQLクエリを購読する場合は、以下のようになります。insert、update、deleteなどでusersテーブルに変更があるたびに、call(Query query)メソッドに実行可能なクエリが流れてきます。

Observable<Query> users = db.createQuery("users", "SELECT * FROM users");
users.subscribe(new Action1<Query>() {
  @Override public void call(Query query) {
    Cursor cursor = query.run();
    // TODO parse data...
  }
});

( https://github.com/square/sqlbrite#usage より引用)

 

また、mapToList(Func1<Cursor, T> mapper)メソッドを使うとクエリの実行結果がCursorとして流れてくるので、エンティティへの変換処理を書いてやれば購読時にエンティティのリストを得ることができます。

db.createQuery("users", "SELECT * FROM users")
    .mapToList(new Func1&lt;Cursor, User&gt;() {
        @Override
        public User call(Cursor cursor) {
            long id = cursor.getLong(cursor.getColumnIndexOrThrow("_id"));
            String name = cursor.getString(cursor.getColumnIndexOrThrow("name"));
            return new User(id, name);
        }
    }).subscribe(new Action1&lt;List&lt;User&gt;&gt;() {
        @Override
        public void call(List&lt;User&gt; users) {
            // ユーザーリストの表示など
        }
    });

結果が1件だけの場合はmapToOne(Func1<Cursor, T> mapper)メソッドを使えば、単一のエンティティに変換できます。

db.createQuery("users", "SELECT count(*) user_count FROM users")
    .mapToOne(new Func1&lt;Cursor, Long&gt;() {
        @Override
        public Long call(Cursor cursor) {
            long count = cursor.getLong(cursor.getColumnIndexOrThrow("user_count"));
            return count;
        }
    }).subscribe(new Action1&lt;Long&gt;() {
        @Override
        public void call(Long count) {
            // ユーザー数の表示など
        }
    });

Pros / Cons

私の考えたSQLBriteのメリット(Pros)とデメリット(Cons)です。

Pros – 画面間のデータの共有に使える

たとえば、画像などのコンテンツにユーザーがコメントしたときに、その画像を表示している全ての画面でコメントを更新したい状況を考えます。SQLBriteを使って、コメントテーブルに対するSQLクエリを購読すれば、コメントの追加(insert)、修正(update)、削除(delete)に対して全ての画面で最新の状態に更新することができます。また、RxLifecycleと併用すれば、Activityが破棄されている場合は変更イベントを受け取らず次の生成時に再購読する、というようにActivityのライフサイクルに対して安全に通知を受け取ることができます。

Cons – SQLを書くコスト

SQLBriteはORMライブラリではないので、データを取得するために、いちいちSQLを書く必要があります。これまでORMを使っていて、そこから乗り換える場合はSQLの学習コストがかかると思います。私はSQLBriteのシンプルさが好きですが、人によってはORMのような手軽さがなくて苦手かもしれません。このあたりは好みの問題だと思います。

まとめ

この記事では、SQLiteのテーブル変更をストリーム化するライブラリSQLBriteを紹介しました。RxJava好きの方は、ぜひ実際に使ってみてストリームの気持ち良さを味わってください。