Realm Java 0.89 — ModelとCollectionのための新インターフェース
Realm Javaの最新版をwebサイトとJCenterでリリースしました。
このリリースには、数多くの新機能とバグフィックスが含まれています。
RealmModelインターフェース
Realmのリリース以来ずっと存在していた制限の一つに、RealmのすべてのModelクラスはRealmObject
を継承しなければならないというものがありました。
0.89のリリース以降この制限は緩和され、代わりにRealmModel
インターフェースを実装することを選択できるようになります。
@RealmClass
public class implements RealmModel {
}
その場合、RealmModel
インターフェースの実装に加えて@RealmClass
アノテーションを付与することも必要になります。これは、インターフェースに付与されたアノテーションは、その実装クラスに対しては引き継がれないという現状の制限によるものです。
RealmObject
が持っていた各種メソッドについてはRealmObject
のstaticメソッドとしても提供されるようになったので、RealmModel
を使用する際はこちらのstaticメソッドをご利用ください。
Person person = getPerson();
// Extending RealmObject
person.isValid();
person.addChangeListener(listener);
// Implementing RealmModel
RealmObject.isValid(person);
RealmObject.addChangeListener(person, listener);
RealmObject
を継承する方法は依然としてRealmが推奨する方法ですが、開発するアプリケーションのアーキテクチャやコードのガイドラインにとって適した方法を選択してください。
Realm Javaは、モデルクラスがRealmObject
以外のクラスを継承してポリモーフィズムを実現することについては依然としてサポートしていません。この機能については、issue761として扱われます。
RealmCollection API
Realm Javaにはコレクションを扱うAPIとしてRealmResults
とRealmList
の2つのクラスが存在していて、それぞれ標準のList
インターフェースを実装しています。
これらのクラスはRealmに関連する機能を持っていますが、その挙動が微妙に異なってていて、RealmBaseAdapter
でこれらを共通に扱おうとする際に問題になっていました。
より一貫性のあるAPIとするため、RealmCollection
とOrderedRealmCollection
の2つのインターフェースを導入します。
これらのインターフェースにより、APIがもつ名前と振る舞いの一貫性が高まります。
今回の変更では特に以下の点にご注意ください。
remove
やclear
が用いられるAPIはコレクションからの要素の削除のみを意味し、Realmのオブジェクト自体の削除は行われません。これは特にRealmResults
の振る舞いを大きく変えることになります。RealmResults
がもつこれらのメソッドについては、コレクションからの要素の削除が行えないためUnsupportedOperationException
をスローします。
-
RealmBaseAdapter
はRealmList
とRealmResults
の両方に対して使用できるようになります。 -
オブジェクトをコレクションとRealmの両方から削除するためには
deleteFromRealm()
またはdeleteAllFromRealm()
を使用してください。 -
Realm.clear(Class)
とRealm.clear()
はそれぞれRealm.delete(Class)
とRealm.deleteAll()
に変更されます。 -
RealmObject.removeFromRealm()
はRealmObject.deleteFromRealm()
へリネームされました。
安定イテレーター
Realmの重要な機能のひとつに検索結果の自動更新があります。多くの場合この機能は素晴らしいものですが、RealmResult
に含まれるオブジェクトに対し、RealmResults
が保持する条件を満たさなくなるような変更を行うケースでは困った問題が発生します。
以下に具体例を示します。
RealmResults<Person> results = realm.where(Person.class).equalTo("inviteToGoogleIO", false).findAll();
// 全員を招待したい
realm.beginTransaction();
for (int i = 0; i < results.size(); i++) {
results.get(i).inviteToGoogleIO(true);
}
realm.commitTransaction();
通常のコレクションであれば、すべての人がGoogle I/Oに招待されることになります。しかしRealmResults
のライブアップデートの機能のために実際には半分の人しか招待されません。というのも、RealmResults
がライブアップデートされることにより、1つ目のオブジェクトのフィールドがtrue
にセットされた瞬間にRealmResults
が更新され先頭の要素が取り除かれてしまいます。このことによりループのインデックスと実際のリストの中身に不整合が発生し、ループが回るたびにひとつオブジェクトを飛ばしてしまいます。
この問題を回避するためにはリストを後ろから更新していく方法がありました。
for (int i = results.size() - 1; i >=0; i--) {
results.get(i).inviteToGoogleIO(true);
}
この方法は直感的ではありません。0.89からはRealmResults
のライブアプデート機能の動作動作タイミングが変更されています。いままでは同じスレッドで更新があると即座に反映されていましたが、0.89以降では更新タイミングがつぎのLooperイベントのタイミングまで遅延されます。
この変更によりループやイテレーターは意図通りに動作するようになりますが、負の側面として削除済みのオブジェクトに意図せずアクセスしてしまう可能性があげられます。
RealmResults<Person> results = realm.where(Person.class).findAll();
for (Person p : results) {
p.deleteFromRealm(); // 間接的にすべてのオブジェクトを削除します
}
// RealmResultsは次のLooperイベントまで更新されないため、
// 更新されるまでの間削除済みのオブジェクトを返します。
Person p = results.get(0);
p.isValid() == false;
// RealmResultsから直接削除した場合はそのRealmResultsからも取り除かれます
results.deleteFromRealm(0);
// さらに、新たに検索した場合も、削除が反映されます。
results = realm.where(Person.class).findAll(); // 削除されたユーザーは含まれません
詳細はこちらを御覧ください。
またこの変更は同じスレッド上で発行されたRealm.commmitTransaction()
によって実行されるすべてのRealmChangeListener
にも影響します。これらのリスナーは他のスレッド上での変更の場合と同じように、次のLooperイベントまで遅延されます。この変更はほとんどのアプリケーションでは影響がありませんが、たとえば変更の通知が即座に行われることを前提としているユニットテストに影響する可能性があります。
互換性のない変更点 🚨
プライマリーキーのフィールドは、暗黙的な@Required
とは扱われなくなります。
また、プライマリーキーとして指定されたフィールドがnull
を 許容する型の場合、null
を代入することも許されるようになりました。
0.89より前のバージョンからアップデートすると、この変更によりプライマリーキー指定がnull
可能な型に付与されていた場合にRealmMigrationNeededException
がスローされます。モデル定義に@Required
を付与して以前と同じ挙動を維持するか、マイグレーションによって以下のようにnull
を許可するようにデータベースを変更するかのいずれかの対処を行ってください。
RealmObjectSchema personSchema = schema.get("Person");
personSchema.setNullable("myPrimaryKey", true);
そのほかの改善点
-
RealmConfiguration.initialData()
が追加されました。Realmファイルが新規に作成される際、同時に初期データを投入することができます(thanks @thesurix)。 -
GSONを使用する際、
ExclusionStrategy
を定義する必要がなくなりました。
完全な変更内容一覧と詳細はchangelogら参照することができます。
お読みいただきありがとうございます。Realmが素晴らしいアプリの実装のお役に立てれば嬉しく思います。お困りの際はStack Overflow(日 本語)、 Slack(日本語)、Twitter(日本語)、GitHub(英語)でご相談ください。