Realm Blog

Realm Java 0.87 — RxJavaサポート!

Realm Javaの最新版をwebサイトMavenリポジトリでリリースしました。

このリリースでは、多くの要望をいただいていたRxJavaサポートが導入されました。

RxJava

RxJavaは、NetflixがリリースしているReactive Extensionsライブラリで、Observerパターンを拡張したものです。RxJavaはデータに対する変更を監視するとともに、様々な処理を組み合わせてデータに適用することを可能にします。

Realmが提供するObservableは、従来提供されていたRealmChangeListenerの仕組みの上に構築されています。そのため、今までRealmChangeListenerが受け取っていたコールバックと同じタイミングでObservableに対する呼び出しが行われます。

Realm realm = Realm.getDefaultInstance();
RealmResults<Person> persons = realm.where(Person.class).findAll();
Person person = persons.first();

Observable<Realm> realmObservable = realm.asObservable();
Observable<RealmResults<Person>> resultsObservable = persons.asObservable();
Observable<Person> objectObservable = person.asObservable();

Realmが提供するAPIには非同期のものもありますが、同様に利用可能です。

realm.where(Person.class).equalTo("name", "John").findAllAsync().asObservable()
  .filter(new Func1<RealmResults<Person>, Boolean>() {
      @Override
      public Boolean call(RealmResults<Person> persons) {
          // 完了していない場合は無視する
          return persons.isLoaded();
      }
  })
  .subscribe(new Action1<RealmResults<Person>>() {
      @Override
      public void call(RealmResults<Person> persons) {
          // personsを表示
      }
  });

様々な例が、RxJava sample projectに収録されています。

RxJavaに関する設定

RxJavaは必須の依存ライブラリではありません。これは、Realmが自動的にRxJavaのライブラリを依存ライブラリに含めてはくれないことを意味しています。Realmを利用するアプリでどのバージョンのRxJavaを使用するか決めることができます。また、RxJavaを含めないようにすることで、メソッド数の増加を抑えることも可能です。

RxJavaを使用する場合は、build.gradleに依存ライブラリとして明示的に追加してください。

dependencies {
  compile 'io.realm:realm-android:0.87.0'
  compile 'io.reactivex:rxjava:1.1.0'
}

Observableインスタンスの生成方法をカスタマイズ可能にするために、RxObservableFactoryインタフェースが追加されています。 このカスタムファクトリは、RealmConfigurationで指定することができますが、指定されない場合はRealmObservableFactoryクラスがデフォルトのファクトリクラスとして使用されます。この場合、RxJava 1.1.*をサポートします。

RealmConfiguration config = new RealmConfiguration.Builder(context)
  .rxFactory(new MyFactory())
  .build()

Work-in-progress

Realm Java 0.87.0はRxJavaをサポートする最初のバージョンですが、RealmQueryRealmListに対するObservableサポートについては今回のバージョンでは提供されていません。これらについては将来のバージョンでサポートすることを予定しています。

また、RealmObjectRealmResultsは依然としてスレッドをまたいだ利用は行えません。

// Observableでよくあるバックグラウンドスレッドの使い方
Observable.defer(new Func0<Observable<RealmResults<Person>>>() {
    @Override
    public Observable<RealmResults<Person>> call() {
        return realm.where(Person.class).findAll().asObservable();
    }
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<RealmResults<Person>>() {
    @Override
    public void call(RealmResults<Person> persons) {
        // personsにアクセスするとスレッド制約違反でクラッシュします
    }
});

// 正しくは、Realmが提供する非同期APIを使用します
realm.where(Person.class).findAllAsync().asObservable()
  .subscribe(new Action1<RealmResults<Person>>() {
      @Override
      public void call(RealmResults<Person> persons) {
          // UIにデータを表示
      }
  });

RealmObjectRealmResultsはライブなオブジェクトなので、Realmの更新に伴って最新の値に更新され続けます。これはデータの一貫性という点でとても素晴らしいことですが、RxJavaのイミュータブルなスレッドセーフオブジェクトと相性が良い設計とは相容れない部分があります。そこで、RxJavaサポートの一環としてRealm.copyFromRealm()メソッドを追加しました。このメソッドは、ライブなRealmObjectの内容をRealmから切り離された通常のオブジェクトにコピーします。この処理は処理時間、メモリのいずれの点においても高価ですが、簡単にbuffer()と組み合わせられるようになります。

// 更新される前と後のPersonを扱う例
realm.where(Person.class).findFirst().<Person>asObservable()
        .map(new Func1<Person, Person>() {
            @Override
            public Person call(Person person) {
                // Realmから切り離されたコピーを作成
                return realm.copyFromRealm(person);
            }
        })
        .buffer(2)
        .subscribe(new Action1<List<Person>>() {
            @Override
            public void call(List<Person> persons) {
                // `map`でRealmから切り離していない場合、v1とv2は両方とも最新に更新されてしまいます
                Person v1 = persons.get(0);
                Person v2 = persons.get(1);
            }
        });

このようなコピーを作成するワークアラウンドは将来的には不要になる予定です(issue 1208)。

詳細な変更点については、changelogを参照してください。


お読みいただきありがとうございます。Realmが素晴らしいアプリの実装のお役に立てれば嬉しく思います。お困りの際はStack Overflow(日本語)Slack(日本語)Twitter(日本語)GitHub(英語)でご相談ください。


Realm Team

At Realm, our mission is to help developers build better apps faster. We provide a unique set of tools and platform technologies designed to make it easy for developers to build apps with sophisticated, powerful features — things like realtime collaboration, augmented reality, live data synchronization, offline experiences, messaging, and more.

Everything we build is developed with an eye toward enabling developers for what we believe the mobile internet evolves into — an open network of billions of users and trillions of devices, and realtime interactivity across them all.

記事の更新情報を受け取る