techium

このブログは何かに追われないと頑張れない人たちが週一更新をノルマに技術情報を発信するブログです。もし何か調査して欲しい内容がありましたら、@kobashinG or @muchiki0226 までいただけますと気が向いたら調査するかもしれません。

QuantumFluxを使ってContentProviderを作成する

AndroidのContentProviderを作るときにsqliteを使っているケースがほとんどだと思います。
しかし昨今のORMではsqliteを作るのは簡単ですが、ContentProviderでの適応が難しいケースが多々あります。

そこでContentProvider用のORMとしてQuantumFluxというのがありましたのでご紹介します。

QuantumFluxはsqliteを内部で持っており、AndroidManifestに記載したDB名やパッケージ名から自動的にデータベースを構築し、ContentProviderとして公開することができます。

それではやってみましょう。

Gradleの設定

app/build.gradleのdependenciesに下記を記載します。

compile 'info.quantumflux:library:0.9.1'

残念ながら公式のREADMEでは取得先が間違っていますので注意が必要です。

Applicationクラスの用意

まずはアプリケーションクラスを用意します。
ここでコンテキストをQuantumFluxに渡し、プロバイダ用のAndroidManifest情報を提供しDBの初期化を行います。
下記のようにします。

public class SampleApplication extends Application {
    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        QuantumFlux.initialize(this);
    }
}

QuantumFlux.initializeでコンテキストを提供すると初期化ができます。

AndroidManifestの用意

ContentProviderに関する定義をAndroidManifestに記載していきます。

次のように記載していきます。

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:name="com.hatenablog.techium.quantumfluxsample.SampleApplication"
        android:theme="@style/AppTheme">

        <meta-data android:name="AUTHORITY" android:value="com.hatenablog.techium.quantumfluxsample" />
        <meta-data android:name="DATABASE_NAME" android:value="Techium.db" />
        <meta-data android:name="DATABASE_VERSION" android:value="1" />
        <meta-data android:name="PACKAGE_NAME" android:value="com.hatenablog.techium.quantumfluxsample" />
        <meta-data android:name="QUERY_LOG" android:value="true" />

        <省略>

        <provider
            android:name="info.quantumflux.provider.QuantumFluxContentProvider"
            android:authorities="com.hatenablog.techium.quantumfluxsample"
            android:exported="false" />
    </application>

に対して前節で作成したApplicationクラスを指定します。
次に内に<meta-data>を下記の内容で記載します。

  • AUTHORITY : 公開するコンテンツプロバイダのAUTHORITYを指定してください。
  • DATABASE_NAME : データベースのファイル名
  • DATABASE_VERSION : DBのバージョン番号
  • PACKAGE_NAME : アプリのパッケージ名
  • QUERY_LOG : QUERY時のログを出すかどうか(オプション)

最後にタグを記載します。 authoritiesは必ずmeta-dataのAUTHORITYと同じものを利用しましょう。
android:nameはinfo.quantumflux.provider.QuantumFluxContentProviderから変更せずにこのまま利用してください。 このContentProviderが要求を受け取りその要求内容に合わせた内容の処理を実行する事になります。
定義自体はQuantumFluxのライブラリ内にあるので興味がある人は見てみてください。

モデル作成

今回はサンプルとしてTaskクラスを作成しそれを利用します。
Taskクラスは次のものを用意しました。

public class Task extends QuantumFluxRecord<Task> {

    public String subject;

    public String detail;
}

ここでの注意点はQuantumFluxRecordを継承しているクラス名をすべて小文字にしたものがuriの指定の一部となります。
今回のケースの場合は「content://com.hatenablog.techium.quantumfluxsample/task」がcontent uriになります。 QuantumFluxRecordはContentProviderに登録されるデータを操作することができるようにするクラスになります。
これを継承することで削除、更新、検索、追加ができます。
また、QuantumFluxRecordを継承しないでTableというアノテーションを利用する方法もありますがそれは検索、追加のみしかできず変更がContentProviderの変更ができないので注意しましょう。

モデルではPrimaryKeyやカラム名を指定するColumnというアノテーションがあります。
PrimaryKeyはプライマリを指定することができますが、QuantumFluxRecordで_idが定義されているためQuantumFluxRecordを継承したクラスではPrimaryKeyを利用できないことに注意しましょう。
Columnはカラム名を指定することなどが可能です。
Columnについての指定方法は公式を参考にしてください。

使ってみる

データの保存

下記のようにデータを入れた状態でsaveを実行すると保存されます。

        Task task = new Task();
        task.subject = "TODO 1";
        task.detail = "記事書く";
        task.save();

データの検索

下記のようにSelect.fromで指定したクラスに該当するデータを列を取得します。
Selectクラスは取得したデータの内の絞り込むwhereやソート機能などのメソッドを持っています。
そしてqueryAsListやfirstやlastなどのメソッドで該当するクラスを取得することができます。

        Select<Task> select = Select.from(Task.class);
        // 最初のデータ取得
        Task task = select.first();

データの更新

下記のようにselectで取り出したデータに対してupdateを実行すると該当のデータを更新することができます。

        Task item = Select.from(Task.class).first();
        item.detail = "更新しました";
        item.update();

QuantumFlux.updateColumnsを使うことで全件の更新が可能と思いますが、残念ながら検索結果に該当するすべてのデータをまとめて更新することができなさそうです。

データの削除

        // 全削除の場合
        QuantumFlux.deleteAll(Task.class);

        // 1検索所の場合
        item = Select.from(Task.class).first();
        item.delete();

コンテンツプロバイダ経由でアクセスしてみる

今回作成したものでデータを5つ登録してみました。
そのデータをcontentコマンドを使ってアクセスしてみましょう。

$ adb shell content query --uri content://com.hatenablog.techium.quantumfluxsample/task
Row: 0 detail=記事書く, subject=TODO 1, _id=1
Row: 1 detail=記事書く, subject=TODO 2, _id=2
Row: 2 detail=記事書く, subject=TODO 3, _id=3
Row: 3 detail=記事書く, subject=TODO 4, _id=4
Row: 4 detail=記事書く, subject=TODO 5, _id=5
$ adb shell content query --uri content://com.hatenablog.techium.quantumfluxsample/task/1
Row: 0 detail=記事書く, subject=TODO 1, _id=1

IDを指定した取得も問題なくできることが確認できます。

感想

すごく簡単にContentProviderを作成することができることやモデルに合わせたデータのアクセスができるためCursorの面倒な処理を書く必要がないことが非常にいいと思われます。
しかし公式のIssueにmulti dexでは動かない報告もありますのでアプリの規模に応じて使うか判断したほうがいいと思われます。

サンプルコード

github.com