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をサポートする最初のバージョンですが、RealmQuery
とRealmList
に対するObservable
サポートについては今回のバージョンでは提供されていません。これらについては将来のバージョンでサポートすることを予定しています。
また、RealmObject
とRealmResults
は依然としてスレッドをまたいだ利用は行えません。
// 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にデータを表示
}
});
RealmObject
とRealmResults
はライブなオブジェクトなので、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(英語)でご相談ください。