古いバージョンのドキュメントを表示しています。
最新版のドキュメントはこちらからご覧になれます

はじめに

Download Realm for Android または、ソースコードは realm-java on GitHub

必要なもの

  • realm-java は、Android プロジェクト以外では、サポートされていません。
  • バージョン0.8.6以上の Android Studio ( Eclipse をお使いの場合は、以下をご覧ください)
  • 最新の Android SDK
  • バージョン7以上のJDK
  • API Level 9 ( Android2.3 Gingerbread ) からのは全てサポートしています。

インストール

Maven を使うまたは、Jar を手動で追加するかで Realm をプロジェクトに追加してください。

Maven

  1. 依存レポジトリのために jcenter を使ってください。( Android Gradle プラグインではデフォルトです。)
  2. compile 'io.realm:realm-android:0.80.3' を追加してください。
  3. Android Studio のメニューから Tools->Android->Sync Project with Gradle Files を選択してください。

Jar

  1. Download から最新のパッケージをダウンロードし解凍してください。
  2. Android Studio で新しいプロジェクトを作成します。
  3. realm-VERSION.jarapp/libs にコピーします。
  4. Android Studio のメニューから Tools->Android->Sync Project with Gradle Files を選択してください。
  1. Download から最新のパッケージをダウンロードし解凍してください。
  2. jarファイルと libtightdb-jni.so を含んだ distribution/eclipse/ にあるフォルダをプロジェクトの libs フォルダにコピーしてください。
  3. libs にある realm の jar ファイルを右クリックし、”Build Path” -> “Add to Build path” を選択してください。
  4. プロジェクトで右クリックをし、”Properties” を選択してください。 “Java Compiler” -> “Annotation Processing” を選択し、 “Enable project specific settings” にチェックを入れ “Apply” をクリックしてください。
  5. 次に “Annotation Processing” -> “Factory Path” を選択し、”Enable project specific settings” をチェックします。”Click Add JARs” で、”libs” にある realm の jar ファイルを選びます。そしてビルドしてください。
  6. アノテーション処理のために、アノテーション @RealmClass を RealmObject のサブクラスを定義するときに入れておくことをオススメします。

ProGuard

Realm は、RealmObject のためにコンパイル時に proxy クラスを生成しています。難読化が行われ、ProGuard でそれらを利用できるようにするために、以下の設定を追加してください。

-keepnames public class * extends io.realm.RealmObject
-keep class io.realm.** { *; }
-dontwarn javax.**
-dontwarn io.realm.**

Realm Browser

Realm Browserは、現在Mac OSXにのみ対応しております。Windows、Linux版は現在、準備中ですので、もうしばらくお待ちください。

RealmBrowserは、.realmデータベースを閲覧、編集するMac アプリケーションです。

Realm Browser

また、Tools > Generate demo database を選択することでサンプルデータを含んだ、テスト用のRealmデータベースを作ることもできます。

開発中のアプリのRealmファイルがどの場所に格納されているかは、このStackOverflowの回答が参考になります。

Realm BrowserMac App Store からダウンロードできます。

APIリファレンス

Realm で使用できる Class, Method に関しては、API Reference をご覧ください。

サンプルコード

最新のRealm には、いくつかのサンプルプロジェクトが含まれています。Android Studio で、Import Project で追加し、run で実行してみてください。

RealmIntroExample は、簡単な API の使い方について学べます。アプリを実行し、出力されるログを見てみてください。

RealmGridViewExample は、GridView での Realm の使い方について学べます。

RealmThreadExample は、マルチスレッドでの Realm の使い方について学べます。

RealmAdapterExample は、RealmBaseAdapter を使って、RealmResults をリストにどのようにバインドするかについて学べます。

RealmJsonExample は、JSON の扱い方について学べます。

RealmEncryptionExample は、Realm を暗号化するやり方について学べます。

ヘルプ

  • 直接質問をしたい場合は、こちらの Slack グループでお気軽にご質問ください(Slack グループは日本語です)
  • バグ報告や機能リクエストについては GitHub レポジトリにご投稿ください。
  • ディスカッション & サポート: [email protected].
  • StackOverflow: 以前の質問は StackOverflow で #realm をご覧ください。
  • Community Newsletter に参加することで定期的に Realm に関するの Tips や UseCase, ブログポストやチュートリアルなど、Realm の最新情報がGetできます。

モデル

Realm で使うモデルクラスは、一般的な Java Bean と同じように定義できます。以下のように RealmObject のサブクラスを作ります。

public class User extends RealmObject {

    @PrimaryKey
    private String          name;
    private int             age;

    @Ignore
    private int             sessionId;

    // Standard getters & setters generated by your IDE…
    public String getName() { return name; }
    public void   setName(String name) { this.name = name; }
    public int    getAge() { return age; }
    public void   setAge(int age) { this.age = age; }
    public int    getSessionId() { return sessionId; }
    public void   setSessionId(int sessionId) { this.sessionId = sessionId; }
}

注意することは、ゲッター/セッターメソッドは RealmObject が作るプロキシクラスによって上書きされることです。 ゲッター/セッターに書かれたロジックは実行されません。 Realm とオブジェクトは強く結びついてるので、realm.createObject() メソッドを使ってインスタンスを作成してください。

フィールドの型

Realm では、以下の型がサポートされています。 boolean, short, int, long, float, double, String, Date, byte[] Realmでは、short, int, long 型が全て long 型として扱われます。また、 RealmObject のサブクラスと RealmList<? extends RealmObject> が関連付けのために使用されます。

無視されるプロパティ

アノテーション @Ignore を付けて宣言したフィールドは、ディスクに保存するときに除外されます。インプットされるデータが余分で使わないフィールドを含んでいる時に、そのフィールドを除外するときに有効となります。

検索インデックス

アノテーション @Index を付けると、検索インデックスを作成することができます。これを作成することで、データが挿入される時は遅く、データファイルは大きくなりますが、クエリ実行時により速く取得することができます。クエリを速く実行する必要があるところでのみ使うことをオススメします。 現在、string 型でのみ(他の型のサポートは近々追加する予定です)使用可能です。検索インデックスは削除することはできません。

プライマリーキー

プライマリーキーを指定するには、@PrimaryKey を使います。指定できるフィールドは、string か integer(short, int, long) である必要があります。複数のフィールドを指定すること(複合プライマリーキー)はできません。String 型のフィールドを Primary キーにすると、暗黙的にそのフィールドにインデックスを貼られます。( @PrimaryKey をつけることで、自動的に @Index が付きます。) Realm オブジェクトが作られるとき、全てのフィールドでデフォルト値がセットされます。すでに存在するデータのプライマリキーとの衝突を避けるために StandAlone オブジェクトを作成した後は、プライマリキーのフィールドに値をセットしてください。copyToRealm() を使い、Realm にオブジェクトをコピーしてください。StandAlone オブジェクトの扱い方については、後のセクションをご覧ください。
また、createOrUpdate() を使うことでオブジェクトを作成し、更新することができます。 このメソッドでは、プライマリキーが一致するデータが既に存在すれば、更新を行い、無ければ作成されます。
プライマリーキーを使うことによってパフォーマンスが向上されます。オブジェクトの作成と更新は、少し遅くなりますが、問い合わせは速くなります。パフォーマンスへの影響は、データセットの大きさによって違います。

書き込み

読み込み処理は、どこからでもクエリを投げることができ、いつでもデータにアクセスできますが、書き込み(追加、変更、削除)処理は、必ずトランザクションの中で行わなければいけません。 Writeトランザクションは、コミットかキャンセルができます。コミットした時にディスクにデータが書き込まれ、全てのデータが保存されるとコミット処理が成功となります。Write トランザクションをキャンセルした場合、全ての変更は破棄されます。Writeトランザクションを使用することで、データの整合性が保たれます。

Write トランザクションは、スレッドセーフを保証するために使用します。

// Obtain a Realm instance
Realm realm = Realm.getInstance(this);

realm.beginTransaction();

//... add or update objects here ...

realm.commitTransaction();

トランザクションの中で作った、Realm オブジェクトの変更は、反映させるかキャンセルするのか選ぶことができます。 変更のコミットをやめ、削除する場合、以下のように簡単にキャンセルすることができます。

realm.beginTransaction();
User user = realm.createObject(User.class);

//  ... 

realm.cancelTransaction();

書き込み処理が行われている間は、他の処理をブロックしていることになります。 UI スレッドで行い、スレッドを止めてしまうと ANR エラーの原因になります。 これを避けるために、トランザクション外でメモリ上にオブジェクトを生成し、トランザクション内では、Realm.copyToRealm() だけをしてあげると、少しのブロックで済むようになります。

Realm は MVCC アーキテクチャーであるため、Write トランザクションが開始されている状態でも、読み込み処理は正しく行えます。 同時に複数のスレッドから書き込みする場合でない限り、長めの Write トランザクションを使うことをオススメします。 Realm に書き込み処理をコミットしたとき、全ての Realm インスタンスは通知を受け、Read トランザクションは自動的に更新されます。 Realm のトランザクションは、ACID に準拠しています。

オブジェクトの作成

Realm オブジェクトのメソッドでオブジェクトを作ると、始めから Realm とオブジェクトが結びついています。

realm.beginTransaction();
User user = realm.createObject(User.class); // Create a new object
user.setName("John");
user.setEmail("[email protected]");
realm.commitTransaction();

また、それとは違って、始めにオブジェクトを作成し、realm.copyToRealm() を使って、後から保存し、紐付けることもできます。 Realm は、引数を取らないコンストラクタを含む、数多くのコンストラクタをサポートしています。

User user = new User("John");
user.setEmail("[email protected]");

// Copy the object to Realm. Any further changes must happen on realmUser
realm.beginTransaction();
User realmUser = realm.copyToRealm(user);  
realm.commitTransaction();

realm.copyToRealm() を使う上での注意点は、返り値として返されたオブジェクトのみ Realm によって管理されているということです。その後、最初に作られたオブジェクトに変更を加えても何も反映されません。

トランザクションブロック

realm.beginTransaction(), realm.commitTransaction(), realm.cancelTransaction() の代わりに、realm.executeTransaction() でブロック形式でトランザクションを記述できます。同様に、エラーが発生したときにキャンセルも行えます。

realm.executeTransaction(new Realm.Transaction() {
	@Override
	public void execute(Realm realm) {
		User user = realm.createObject(User.class);
		user.setName("John");
		user.setEmail("[email protected]");
	}
});

クエリ

Realm でのフェッチ(クエリを含む)は全て遅延評価です。また、内部で使われるデータはコピーされません。

Realm のクエリの仕組みは複数の条件を指定できるように Fluent interface を使っています。 name の値が JohnPeter である全てのオブジェクトを取得の仕方は以下の通りです。

// Build the query looking at all users:
RealmQuery<User> query = realm.where(User.class);

// Add query conditions:
query.equalTo("name", "John");
query.or().equalTo("name", "Peter");

// Execute the query:
RealmResults<User> result1 = query.findAll();

// Or alternatively do the same all at once (the "Fluent interface"):
RealmResults<User> result2 = realm.where(User.class)
                                  .equalTo("name", "John")
                                  .or()
                                  .equalTo("name", "Peter")
                                  .findAll();

この例では、name の値が JohnPeter であるデータが RealmResults として返ってきます。この時、データはコピーされません。 該当するオブジェクトのリストの参照を取得することができ、クエリで取得できるオブジェクトを直接、操作することができます。

RealmResult は、Java の AbstractList を継承しており、その振る舞いは似ています。 例えば、RealmResult は、順番を持っていて、インデックスを指定しアクセスできます。

実行されたクエリが何もマッチしなかった場合、返り値として返される RealmResult オブジェクトは、null ではありません。しかし、その場合の size()メソッドは、0を返します。

RealmResult オブジェクトに対して変更を加えたり、削除したりを行う場合、それらの操作は、Write トランザクションの中で行わなければいけません。

型によるオブジェクトの取得

Realm からオブジェクトを取得するときの一般的なメソッドは、realm.allObjects()です。このメソッドは、指定のモデルクラスのオブジェクト全てのデータを RealmResult として返します。 また、allObjects() に似たメソッドで realm.allObjectsSorted() もあります。これを使うと取得時に、フィールドを指定してソートしたりすることができます。
詳しくは、realm.allObjectsSorted() をご覧ください。

条件文

以下の条件文がサポートされています。

  • between, greaterThan(), lessThan(), greaterThanOrEqualTo(), lessThanOrEqualTo()
  • equalTo(), notEqualTo()
  • contains(), beginsWith(), endsWith()

ただし、全てのデータ型が対応しているわけではありません。詳しくは、RealmQuery をご覧ください。

Modifiers

文字列の条件で、CASE_INSENSITIVE を使うことで、アルファベットの大文字と小文字の区別を無くすことができます。

論理式

すべての条件文は論理積です。論理和を使う場合は or() を使用します。 グループを特定するためにグループ挿入句を使用します。以下のように beginGroup() で始め、endGroup() で終わります。

RealmResults<User> r = realm.where(User.class)
                            .greaterThan("age", 10)  //implicit AND
                            .beginGroup()
                                .equalTo("name", "Peter")
                                .or()
                                .contains("name", "Jo")
                            .endGroup()
                            .findAll();

さらに、not() を使うことで、取り除くこともできます。not() は、beginGroup()/ endGroup() の中でのみ使用することができます。

ソート

クエリの実行後は、以下のように結果をソートすることができます。

RealmResults<User> result = realm.where(User.class).findAll();
result.sort("age"); // Sort ascending
result.sort("age", RealmResults.SORT_ORDER_DESCENDING);

連続したクエリの実行

連続してクエリを実行することで、データをフィルタリングしていくことができます。

RealmResults<Person> teenagers = realm.where(Person.class).between("age", 13, 20).findAll();
Person firstJohn = teenagers.where().equalTo("name", "John").findFirst();

集約

RealmResults には様々な集約メソッドがあります。

RealmResults<User> results = realm.where(User.class).findAll();
long   sum     = results.sum("age").longValue();
long   min     = results.min("age").longValue();
long   max     = results.max("age").longValue();
double average = results.average("age");

long   matches = results.size();

反復処理

RealmResults は、イテレーションのために Iterable インターフェイスを実装しています。

RealmResults<User> results = realm.where(User.class).findAll();
for (User u : results) {
    // ... do something with the object ...
}

もちろん、以下のような for ループも使用できます。

RealmResults<User> results = realm.where(User.class).findAll();
for (int i = 0; i < results.size(); i++) {
    User u = results.get(i);
    // ... do something with the object ...
}

削除

クエリの結果を使って Realm からデータを削除することができます。

// obtain the results of a query
RealmResults<Dog> results = realm.where(Dog.class).findAll();

// All changes to data must happen in a transaction
realm.beginTransaction();

// remove single match
results.remove(0);
results.removeLast();

// remove a single object
Dog dog = results.get(5);
dog.removeFromRealm();

// Delete all matches
results.clear();

realm.commitTransaction()

Realms

Realm クラスのインスタンスは、データベースと同等のようなものです。異なるオブジェクトを保持しており、これらは一つのファイルに保存されています。

デフォルトRealm

Realm.getInstance(Context context) を呼び出すことで、realm 変数を初期化してることは、すでにお気付きのことでしょう。 このコンストラクタは、今いるスレッドで使える Realm インスタンスを返します。default.realm で呼ばれるファイルは、Context.getFilesDir() が指すディレクトリにあります。 これは、プロジェクトフォルダのルートにある、File フォルダの中にある default.realm のものです。

ファイルは、書き込み可能なディレクトリのルートに作成されます。 ほとんどの場合、/data/data/files/ にファイルがあることでしょう。

realm.getPath()を使えば、Realm ファイルへのパスを確認できます。 注意することは、Realm インスタンスは、スレッドシングルトンであり、このことは、Realm ファイルへの Realm インスタンスを取得したとき、それぞれのスレッドで同じインスタンスが返されることを意味します。

その他のRealm

場合によっては、複数の Realm を使いたい時があります。たとえば、機能ごとに異なるグループ分けされたデータベースがある場合などや、Read-Only なデータとユーザーが編集可能なデータをはっきりと分けたい場合などです。

Realm realm = Realm.getInstance(this, "allmymovies.realm");

スレッド間での実行

スレッド間での使用方法として注意することは、RealmRealmObjectRealmResults インスタンスは、スレッド間での受け渡しができないということです。
複数のスレッドで同じデータにアクセスしたい場合は、それぞれのスレッドで別々の Realm インスタンスを( Realm.getInstance(Context context) や似たようなメソッドを使って)作成する必要があります。それに対してクエリを投げることでオブジェクトを取得する必要があります。 別々のスレッドから同じ Realm ファイルにアクセスすることができ、それぞれのスレッドで読み書きができます。

Realm インスタンスの閉じ方

メモリとファイルディスクリプタの解放のために、Realm は、Closeable を実装しています。Realm インスタンスを使い終わった後に、close することを忘れないでください。 Realm インスタンスはリファレンスカウントで管理されています。これは、もし同じスレッドで二度、getInstance() を呼んだ場合、同様に、close() を二度、呼ばなければいけません。 このことは、どのスレッドで実行されているのか意識することなく、Runnable クラスを実装することができます。 単に、getInstance() で始め、終わりに close() してあげればいいのです。 UI スレッドでの、一般的な方法は、onDestroy() メソッドで、realm.close() を呼ぶことです。 また、以下のように AsyncTask を使っての非同期処理にすることは良い書き方です。

protected Long doInBackground(Context... contexts) {
    Realm realm = null;
    try {
        realm = Realm.getInstance(contexts[0]);

        // ... Use the Realm instance
    } finally {
        if (realm != null) {
            realm.close();
        }
    }
}

また、Looper を使う場合、以下のように書きます。

public class MyThread extends Thread {
    private final Context;

    public MyThread(Context context) {
        this.context = context;
    }

    public void run() {
        Looper.prepare();
        Realm realm = null;
        try {
            realm = Realm.getInstance(context);

            //... Setup the handlers using the Realm instance
            Lopper.loop();
        } finally {
            if (realm != null) {
                realm.close();
            }
        }
    }
}

アプリの API レベルが minSdkVersion >= 19 であれば、try-with-resources 構文が使え、以下のように書けます。

try (Realm realm = Realm.getInstance(context)) {
	// No need to close the Realm instance manually
}

自動リフレッシュ

Looper と紐付いてスレッドで Realm インスタンスが生成された場合、Realm インスタンスは、自動更新機能を持つことになります。これは、Realm インスタンスは、イベントループの中で、自動的に最新のバージョンに反映されるということです。非常に便利な機能であり、少ない労力で、継続的にUIの値などを更新していくことができます。 Looper が紐付いていないスレッドでRealmインスタンスを取得した場合、refresh() メソッドを呼ばない限り、自動更新はされません。

ここで、注意するべきことは、古いバージョンを維持し続けることは、メモリやディスクの観点から見ても、コストのかかることであるということです。 そして、そのコストは新しいバージョンに更新されるにつれ、増加していきます。 これは、Realmインスタンスを使い終わった後、すぐに close() をすべき理由でもあります。 自動更新機能が使えるかどうかの確認は、isAutoRefresh() メソッドでできます。

Realmファイルの探し方

Realm ファイルの特定の仕方が分からない方は、この StackOverflow の回答をご覧ください。

関連

RealmObject は、お互いに関連付けを行うことができます。

public class Email extends RealmObject {
    private String address;
    private boolean active;
    // ... setters and getters left out
}

public class Contact extends RealmObject {
    private String name;
    private Email email;
    // ... setters and getters left out
}

リレーションシップは、スピードの観点からいうと特にオーバーヘッドなく素早く動きます。また、メモリの消費量の点でも効率的に機能します。

多対一

モデル間で一対一の関連性を持たせたい場合は、RLMObject モデルクラスのフィールドを宣言します。

public class Contact extends RealmObject {
    private Email email;
    // Other fields…
}

これで、Contact インスタンスでは、Email インスタンスと関連付けを行うことができます。Realm において、複数の Contact インスタンスから一つの Email インスタンスの関連を持つこともできます。このモデルでは、多対一の関連付けも行うことができますが、たいてい一対一の関連付けで使われると思います。

多対多

RealmList<T> フィールドを追加することで対多の関連を持たせることができます。

public class Contact extends RealmObject {
    private RealmList<Email> emails;
    // Other fields…
}

RealmList クラスは、基本的に RealmObject のコンテナクラスです。振る舞いは、Java の List クラスと似ています。異なる RealmList から一つのオブジェクトへの関連付けを行うことはできます。特に制約はありません。 また、これらは一対多、多対多で使うことができます。ゲッター/セッターを関連付けのフィールドのために追加することができます。

realm.beginTransaction();
Contact contact = realm.createObject(Contact.class);
contact.setName("John Doe");

Email email1 = realm.createObject(Email.class);
email1.setAddress("[email protected]");
email1.setActive(true);
contact.getEmails().add(email1);

Email email2 = realm.createObject(Email.class);
email2.setNumber("[email protected]");
email2.setActive(false);
contact.getEmails().add(email2);

realm.commitTransaction();

再帰的な関連付けの定義もできます。

public class Person extends RealmObject {
    private String name;
    private RealmList<Person> friends;
    // Other fields…
}

現在、Realm では循環された関連を見つける仕組みはまだありませんので、簡単に無限ループを発生しかねます。使用される場合は、十分ご注意ください。

関連付いてるオブジェクトに対してクエリを実行することも可能です。
以下のモデルを考えてみて下さい。emailactive=trueContact インスタンスだけを取得したい場合はこのようにします。

RealmResults<Contact> contacts = realm.where(Contact.class).equalTo("emails.active", true).findAll();

まず、equalsTo を使ってフィールドの状態を調べていることが分かると思います。

上記のクエリは、「少なくとも一つは active フィールドが true な mail インスタンスを持った Contact インスタンスを取得してください」という意味になります。 ここでは、取得される Contact インスタンスの、全ての Email オブジェクトが active=true ではないことを理解することが重要です。

JSON

JSON データから直接、Realm オブジェクトを作ることができます。読み込む JSON データは、String、JSONObject、InputStream であれば問題ありません。Realm は、RealmObject に定義がないプロパティが、JSON に含まれていた場合は、無視します。 一つのオブジェクトを作る場合は、Realm.createObjectFromJson() を使い、JSON データを全てオブジェクトにする場合は、Realm.createAllFromJson() を使用します。

// A RealmObject that represents a city
public class City extends RealmObject {
    private String city;
    private int id;
    // getters and setters left out ...
}

// Insert from a string
realm.beginTransaction();
realm.createObjectFromJson(City.class, "{ city: \"Copenhagen\", id: 1 }");
realm.commitTransaction();

// Insert multiple items using a InputStream
InputStream is = new FileInputStream(new File("path_to_file"));
realm.beginTransaction();
try {
    realm.createAllFromJson(City.class, is);
    realm.commitTransaction();
} catch (IOException e) {
    realm.cancelTransaction();
}

通知

バックグランド処理など他のスレッドやプロセスで Realm の状態に変更があった時に、Listener を追加しておくことで、通知を受け取ることができます。

realm.addChangeListener(new RealmChangeListener() {
    @Override
    public void onChange() {
        // ... do something with the updates (UI, etc.) ...
    }
});

また、Listeners を簡単に削除することができます。

realm.removeAllChangeListeners();

マイグレーション

マイグレーションは、work in progress な機能です。機能的には完全に動作します。しかし、現在のインターフェイスは、複雑ですので書き換える予定です。

データベースを使ってる場合、時間が経つにつれ、データモデルというのは変更されていくものです。 Realm でのデータモデルは、一般的なクラスの定義と同じで、Realm モデルクラスに変更を加えるだけで、簡単にデータモデルを変更できます。

まだ、古いスキーマのデータがディスクに保存されていないなら、変更を加えても問題なく動きます。しかし、古いスキーマのデータがディスクに保存されている場合、ディスクに保存されているスキーマと Realm モデルクラスとして定義されてるスキーマでミスマッチが起こり、例外が投げられます。

マイグレーションのための用意されてるメソッドを使って、ディスクにあるデータを新しいスキーマに適応させる必要があります。 詳しくは、migration sample app をご覧ください。

Realm が使われたときにディスクに Realm のファイルがない場合は、マイグレーションの必要はありませんし、その場合は、Realm はその時のスキーマのモデル定義を確認し新しく .realm ファイルを作成します。これは、開発の最中で頻繁にスキーマに変更があり、全てのデータが失われても問題ないというときは、マイグレーションを行う代わりに .realm ファイルを一度削除するやり方も考えられます。開発の初期段階でモデルに頻繁に変更がある場合などで有効なやり方だと思います。

暗号化

Please take note of the Export Compliance section of our LICENSE, as it places restrictions against the usage of Realm if you are located in countries with an export restriction or embargo from the United States.

Realm.getInstance() の呼び出し時に、512ビット長の暗号キーを使って、Realm ファイルを暗号化してディスクに保存することができます。

byte[] key = new byte[64];
new SecureRandom().nextBytes(key);
Realm realm = Realm.getInstance(this, key);

// ... use the Realm as normal ...

これで AES256 を使って、暗号化し保存することができます。 Realm ファイルが作成された時に使用された暗号化キーと同じものを毎回、使われなければいけません。

詳しくは、examples/encryptionExample をご覧ください。 ただし、他のアプリケーションからは、このファイルを読み込むことはできません。

アダプタ

Realm は、RealmResults として取得したデータを UI に反映させるための抽象クラスがあります。 RealmBaseAdapter は、getView()メソッドを実装することで、データの UI へのバインディングを行ってくれます。

public class Person extends RealmObject {
    private String name;
    public String getName() {
        return this.name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

public class MyAdapter extends RealmBaseAdapter<Person> implements ListAdapter {

    private static class MyViewHolder {
        TextView name;
    }

    public MyAdapter(Context context, int resId,
                     RealmResults<Person> realmResults,
                     boolean automaticUpdate) {
        super(context, realmResults, automaticUpdate);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder;
        if (convertView == null) {
            convertView = inflater.inflate(android.R.layout.simple_list_item_1, 
                                           parent, false);
            viewHolder = new ViewHolder();
            viewHolder.name = (TextView) convertView.findViewById(android.R.id.text1);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }

        Person item = realmResults.get(position);
        viewHolder.name.setText(item.getName());
        return convertView;
    }

    public RealmResults<Person> getRealmResults() {
        return realmResults;
    }    
}

他のライブラリ

このセクションでは、Android でよく使われる他のライブラリと Realm の統合の仕方について説明します。

GSON

GSON は、JSON をシリアライズ/デシリアライズする Google 製のライブラリです。Realm と Gson (最新のバージョン: 2.3.1)を使用する場合、ExclusionStrategy をきちんと指定する必要があります。

// Using the User class
public class User extends RealmObject {
    private String name;
    private String email;
    // getters and setters left out ...
}

Gson gson = new GsonBuilder()
        .setExclusionStrategies(new ExclusionStrategy() {
            @Override
            public boolean shouldSkipField(FieldAttributes f) {
                return f.getDeclaringClass().equals(RealmObject.class);
            }

            @Override
            public boolean shouldSkipClass(Class<?> clazz) {
                return false;
            }
        })
        .create();

String json = "{ name : 'John', email : '[email protected]' }";
User user = gson.fromJson(json, User.class);

GSON と Realm を使ったサンプルコードとして、GridViewExample もご参照してください。

GSON と プリミティブ型

JSON API は Integer や String 型のようなプリミティブ型の配列を返します。それらはまだ Realm ではサポートされていません。JSON API を変更するのが不可能な場合、TypeAdapter を使い JSON のプリミティブ型を Realm オブジェクトに自動でマッピングするようにカスタマイズすることができます。この Gist が Integer を使ったサンプルとなります。

Otto

Otto は、イベントバスを扱うための、Square 製のライブラリです。Realm と Otto は動作します。しかし、いくつか問題があります。

Otto は、基本的にイベントが送られた同じスレッドでイベントを受け取ります。RealmObject をイベントに追加し、受け取ったメソッドでそのデータを読むことは問題なくできます。 しかし、メインスレッドでイベントをポストするために、ここに紹介されているようなHackをした場合、RealmObjects を持たない、イベントデータを受け取ることになります。

Realm は、あるスレッドで RealmObject に変更があった時、他のスレッドで Handler を使い、データを更新しようとします。 一方で、Otto.post(event) は、即座にイベントを送ります。Realm データに変更があったことを他のスレッドに通知するイベントを送った場合、手動で、最新のデータに更新するために realm.refresh() を呼ぶ必要があります。

@Subscribe
public void handleEvent(OttoEvent event) {
    realm.refresh();
    // Continue working with Realm data loaded in this thread
}

Retrofit

Retrofit は、REST API との統合を簡単にする Square 製のライブラリです。 Retrofit は、内部で GSON を使っており、同様に JSON から RealmObjects に変換するのであれば、GsonConverter を適切に設定する必要があります。

Gson gson = new GsonBuilder()
        .setExclusionStrategies(new ExclusionStrategy() {
            @Override
            public boolean shouldSkipField(FieldAttributes f) {
                return f.getDeclaringClass().equals(RealmObject.class);
            }

            @Override
            public boolean shouldSkipClass(Class<?> clazz) {
                return false;
            }
        })
        .create();

// Configure Retrofit to use the proper GSON converter
RestAdapter restAdapter = new RestAdapter.Builder()
    .setEndpoint("https://api.github.com")
    .setConverter(new GsonConverter(gson))
    .build();

GitHubService service = restAdapter.create(GitHubService.class);

Retrofit は、自動的に Realm にオブジェクトを追加してくれません。以下のように realm.copyToRealm() メソッドを使って追加していく必要があります。

GitHubService service = restAdapter.create(GitHubService.class);
List<Repo> repos = service.listRepos("octocat");

// Copy elements from Retrofit to Realm to persist them.
realm.beginTransaction();
List<Repo> realmRepos = realm.copyToRealm(repos);
realm.commitTransaction();

Robolectric

Robolectric は、デバイスやエミュレータの代わりに JVM 上で直接、JUnit テストを動作させるライブラリです。現在、Robolectric を使って Realm のテストを行うことはできません。
詳しくは、フィーチャーリクエストの GitHub Issues をご覧ください: https://github.com/robolectric/robolectric/issues/1389

次のステップ

Realm をより深く理解する、次のステップとして、サンプルコードを用意しています。
HAPPY HACKING!!
Google グループ Realm で、いつでもRealmデベロッパーと議論ができます。

開発中の機能

Realm は、現在β版としてリリースされています。バージョン1.0に向けて、機能追加、バグの修正などを行っています。私たちは、次に導入予定のリストを作成しました。 詳しくは、GitHub issues をご覧ください。

一般的な制約

Realm は、柔軟性とパフォーマンスのバランスを上手く保つため、保存するデータに対していくつか制約があります。

  1. クラス名は、57文字が上限です。Realm の内部では、class_ という文字をクラス名の前に付けて扱われます。ブラウザはその名前の一部を表示します。
  2. フィールド名の63文字が上限です。
  3. Date 型のフィールドは、保存されると少数点以下が切り捨てられます。それと、32bit版と64bit版の互換性を保持するために1900-12-13より前と2038-01-19年よりも後の日付は保存することができません。
  4. ネストしたトランザクションはサポートしていません。検出された場合、例外が投げられます。
  5. 文字列とバイトの配列( byte[] )は、16MB以上は保存できません。
  6. 大文字小文字を区別しない文字列マッチの場合、Latin Basic, Latin Supplement, Latin Extended A, Latin Extended B の文字セット(UTF-8の範囲は、0-591)のみサポートしています。

オブジェクト

プロキシクラスが getter/setter をオーバーライドするため、モデルクラスでの制約がいくつかあります。

  • private インスタンスフィールドのみ
  • デフォルトの setter/getter メソッドのみ
  • public か private な static フィールド
  • static メソッド
  • interface の実装

これらは現在 toString()equals() などをオーバーライドして RealmObject を拡張することができないということです。interface を実装することのみが可能です。これらについては現在開発中です。

ソート

現在、ソートは、Latin Basic, Latin Supplement, Latin Extended A, Latin Extended B の文字セット(UTF-8の範囲は、0-591)でのみサポートしています。それ以外の文字セットでソートを行われた場合、RealmResults オブジェクトは変化しません。

case insentive なクエリ

case sensitive フラグをセットした時 equalTo, contain, endsWith, beginsWith は英語のみ動作します。詳しくはこちらをご覧ください。

スレッド

Realm は、複数のスレッドで同じ Realm ファイルを扱うことはできるのですが、スレッド間で、Realm, Realm オブジェクト, クエリ, RealmResult を渡すことはできません。 さらに、非同期のクエリは、現在サポートされていません。マルチスレッドでの Realm の扱い方については、詳しくは、thread example をご覧ください。

null値

値型( boolean, integer, floating-point number, date, string ) に null を保存することはできません。null 値については、現在、開発中ですが、しかし、完全にサポートしたとしても、null かどうかを判定するための余分な boolean のフィールドを追加することをお勧めします。

マイグレーション

マイグレーションは、機能的には完全に動作します。しかし、現在のインターフェイスは、複雑ですので書き換える予定です。 現在のマイグレーションについては、migration example をご参照ください。

複数のプロセスからのRealmファイルへのアクセス

複数のスレッドから同時に Realm ファイルへのアクセスはできますが、プロセスの場合は、シングルプロセスからのアクセスしかできません。Realm ファイルを異なるプロセスで使用するときは、Realm ファイルをコピーするか新しく作成するかしてください。マルチプロセスのサポートは現在進行中です。

FAQ

どのようにすれば、Realm ファイルを閲覧することができますか?

この stackoverflow の質問で、Realm ファイルの保存場所について説明しています。 Realm ファイルの中身については、Realm Browser を使うことで閲覧できます。

Realm は、どれくらいの大きさですか?

アプリをリリースビルドすると、大半の場合は、800KBぐらいになり APK に追加されます。 配布されている Realm は、複数のアーキテクチャサポート( ARM7, ARMv7, ARM64, x86, MIPS )を含んでいるため、著しく大きくなっています。 APK ファイルは、複数のアーキテクチャの実行ファイルを含んでいますが、Android インストーラは、アプリインストール時にそのデバイスのアーキテクチャ用のネイティブコードのみをインストールします。 最終的に、インストールされるアプリは、APK ファイルよりも小さくなります。

Realm をプロダクション環境で使うことはできますか?

Realm は、2012年から商業利用がされています。 ご利用される場合は、Realm の Java API が、頻繁に変わるものだとお考えの上、Community Feedback を確認しながらお使いください。 機能追加、バグ修正も同様にお考えください。

Realm を使うのにお金を払わないといけませんか?

いいえ、Realm は、完全に無料です。商業利用も可能です。

どのようなビジネスプランなのですか?

すでにエンタープライズ向けの商品の販売や、周辺サービスによって収益を得ています。もし、現在リリースされているものや realm-java で更に必要なものがあれば、いつでもメールで、お気軽にご連絡ください。また私たちのビジネスとは関係なく、realm-javaはオープンに開発をつづけていき、Apache License 2.0の元にオープンソースで公開し続けます。

“tightdb” や “core” という文字をコードの中で見たのですが、これは何ですか?

TightDB というのは、C++ で実装されたストレージエンジンの名前です。現在、オープンソースではありませんが、Apache License 2.0として公開することを検討中です。
バイナリリリースは、Realm Core (TightDB) Binary License として利用可能です。

一般的な Java オブジェクトと Realm オブジェクトの違いはありますか?

主な違いは、Java オブジェクトは、データの値を保持しているが、一方で、Realm オブジェクトは、データそのものは保持しておらず、必要なときに直接、DB からセット/ゲットしてくるようになっています。 このことにより2つの影響があります。一つは、Realm オブジェクトは、一般的な Java オブジェクトよりも軽量です。二つ目は、Java オブジェクトは、値が更新されると手動で更新する必要がありますが、Realm オブジェクトは、いつも最新の値が自動で反映されます。

なぜモデルクラスの定義で、RealmObject を継承する必要があるのですか?

主な理由は、モデルクラスに特定の機能を追加するためです。現在、removeFromRealm()のみ、サポートしています。他の機能もサポートする予定で、コードをより読みやすく、使いやすくするために一般的なAPIをご使用ください。

なぜモデルクラスは RealmObject を継承する必要があるのですか?

モデルクラスに Realm 特有の機能を持たせるためです。現在 removeFromRealm() のみがありますが、今後追加されていく予定です。

*RealmProxy クラスとは何ですか?

RealmProxy クラスは、データの値をオブジェクトに持たせず、DB のデータに直接、読み書きすることを実現するのに使われるクラスです。 Realm のアノテーションプロセッサが、プロジェクト内の各モデルクラスの RealmProxy クラスを生成します。このクラスは、モデルクラスを拡張し、Realm.createObject() が呼ばれたときに、返されます。 しかし、実際に IDE を使って使っている時には、それらの違いを感じないと思います。

なぜ、全てのフィールドで、getter/setter が必要なのですか?

これは、Proxy クラスの動作のせいです。 全てのフィールドのアクセッサは、Proxy クラスによって、オーバーライドされます。こういったことは、Java のプライベートなフィールドのゲッタ/セッタでのみ可能です。 これにより、Comparable のようなインターフェイスを実装した場合と同様に、 全てのプライベートメソッドでそのゲッタ/セッタを使う必要がでてきます。 実際、これが一番の理想的な解決方法とは思いません。なので、この制約を取り除きたいと思っています。AspectJJavassist のような解決策は、そういったことを可能にしてくれますが、私たちは、他の可能性を探し続けています。 Proxy クラスのセッタ/ゲッタのオーバーライドによって、自分でカスタマイズしたセッタ/ゲッタを定義することはできません。一時的な解決策は、@Ignore アノテーションを付けて、セッタ/ゲッタを定義することです。 その時の、モデルクラスは以下のようになります。

package io.realm.entities;

import io.realm.RealmObject;
import io.realm.annotations.Ignore;

public class StringOnly extends RealmObject {

    private String name;

    @Ignore
    private String kingName;

    // custom setter
    public void setKingName(String kingName) { setName("King " + kingName); }

    // custom getter
    public String getKingName() { return getName(); }

    // setter and getter for 'name'
}

上のコードのように、setName() の代わりに、setKingName() を使うようにします。カスタムセッタの中で、setName() を使い、name フィールドに直接、値を割り当てなくします。

なぜ RealmList をインスタンス化することはできないのですか?

RealmList は、普通のリストのように見えますが、実はそうではありません。リレーションシップを表現するもので、Realm によって管理される必要があります。 しかし、標準のリストに簡単にマッピングする機能を RealmLists に実装することも考えています。

なぜ、RealmObject をインスタンス化してはいけないのですか?

これは、RealmList と同じ理由です。RealmObject は、データベースとのプロキシの役割を果たします。なので、Realm によって適切に管理される必要があります。 しかし、これも RealmLists と同様に、モデルオブジェクトから RealmObject に変換する機能を実装することも考えています。

なぜ、書き込み処理を行うときにトランザクションを使う必要があるのですか?

トランザクションは、複数のスレッドからの書き込みに対するアトミック性を保証するためのものです。

トランザクションの範囲をはっきりさせることによって、完全にコミットするか全てを戻すか、どちらか選べるようになります。(エラーが起こった場合は、ロールバックします) また、トランザクションの範囲を明確にしておくことで、更新をコントロールすることができます。(たとえば、複数の Insert を一つの操作として扱えます) SQLite のような SQL ベースのデータベースで、複数のフィールドを一度に更新するような操作をしたとします。この時、トランザクションで自動的にラップされていますが、普通、ユーザーからは見えません。 しかし、Realm では、トランザクションはいつも明示的に書きます。

メモリ不足の例外が出た時はどうすべきですか?

Realm は、組み込みストレージエンジンです。ストレージエンジン自体は、JVM のヒープ上にアロケートはしません。しかし、アプリ自体が、メモリ不足になる可能性があります。 そのような状況では、Realm は、io.realm.internal.OutOfMemoryError Exception を投げます。この例外は無視するべきではなく、もし無視した場合、壊れた状態の Realm ファイルが残ることになります。

ファイルサイズと中間バージョンについて

誰もが、SQLite を使ってデータを保存した時よりもディスクの使用容量が少なくなることを期待されることかと思います。 Realm ファイルの容量が非常に大きくなった場合、Realm がオブジェクトをトラッキングするために中間データを作っているためです。 データの一貫性を保つために、データをフェッチしたときのみバージョンがアップデートされます。 このことは、Realm からデータを読み込み、別のスレッドでそのデータの更新が行われた場合、読み込まれたデータの情報はアップデートされないということです。 そして、Realm は、この中間バージョンを管理するために、内部でのみ使用されるデータが増えていき、その結果、ディスクの使用領域が肥大化してしまうことになります。 (この余分なスペースは、今後の書き込みで再度、使われるか、圧縮されることになります。例えば、compactRealmFile が呼ばれた時などです)

‘Annotation processor may not have been executed.’ という例外はどういう意味ですか?

アプリ実行の際、モデルクラスを読み込み、Proxy クラスが生成されます。 アノテーションプロセスが失敗した場合、Proxy クラスとそれらのメソッドは見つかりません。 アプリが起動時に、Realm が、’Annotation processor may not have been executed.’ と例外を投げます。 Java6 を使っている場合、Inherited アノテーションがサポートされていませんので、この例外が投げられると思います。 @RealmClass をモデルファイルの前に追加する必要があります。そうしない場合は、生成されたファイルを全ての削除、クリーンし、リビルドし直してください。

Kotlin はサポートされていますか?

現在、Realm は公式には Kotlin のサポートはしていません。しかし Realm と Kotlin のユーザーである Kevin Griffin の GitHub public レポジトリの Realm examples in Kotlin があります。Kotlin から Realm を使うときに参考になると思います。

Android Chromium WebView

Lolipop 5.0 では Android の Chromium WebView が使われています。しかし、残念なことに最新の WebView(v40) では暗号化された Realm と一緒に使うとクラッシュするバグがあります。 現在これを回避する方法は WebView を v39にダウングレードするか、暗号化された Realm を開く前に CookieManager.getInstance() を呼ぶことです。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_webview);
    CookieManager.getInstance(); // Prevent Realm from crashing
    Realm realm = Realm.getInstance(this, getKey())
}

詳しくはこちらをご覧ください。