Realm Blog

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としてRealmResultsRealmListの2つのクラスが存在していて、それぞれ標準のListインターフェースを実装しています。

これらのクラスはRealmに関連する機能を持っていますが、その挙動が微妙に異なってていて、RealmBaseAdapterでこれらを共通に扱おうとする際に問題になっていました。

より一貫性のあるAPIとするため、RealmCollectionOrderedRealmCollectionの2つのインターフェースを導入します。

これらのインターフェースにより、APIがもつ名前と振る舞いの一貫性が高まります。

今回の変更では特に以下の点にご注意ください。

removeclearが用いられるAPIはコレクションからの要素の削除のみを意味し、Realmのオブジェクト自体の削除は行われません。これは特にRealmResultsの振る舞いを大きく変えることになります。RealmResultsがもつこれらのメソッドについては、コレクションからの要素の削除が行えないためUnsupportedOperationExceptionをスローします。

  • RealmBaseAdapterRealmListRealmResultsの両方に対して使用できるようになります。

  • オブジェクトをコレクションと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);

そのほかの改善点

完全な変更内容一覧と詳細は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.

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