Realm Blog

Realm Java 0.89 — Model and Collection interfaces!

We just released a new version of Realm Java to this website and to Bintray. This release is packed with fixes and new features.

RealmModel interface

A longstanding requirement for Realm has been that all Realm model classes must extend the base class RealmObject.

Starting with 0.89, this is no longer a strict requirement and you can now choose to implement the new RealmModel interface instead.

@RealmClass
public class implements RealmModel {
	
}

You will need to manually add the @RealmClass annotation since inheriting annotations from interfaces is not yet supported by Android.

The helper methods on RealmObject are available as static methods, so if you use RealmModel you can use these methods.

Person person = getPerson();

// Extending RealmObject
person.isValid();
person.addChangeListener(listener);

// Implementing RealmModel
RealmObject.isValid(person);
RealmObject.addChangeListener(person, listener);

Extending RealmObject is still the recommended approach, but you now have the option of using the method which suits your architecture and codebase guidelines better.

Note that Realm Java still does not support extending anything other than RealmObject. That feature is still being tracked here.

RealmCollection API

The Realm Java API currently consists of two collections: RealmResults and RealmList, which both implement the standard List interface.

Both of these classes have been extended with additional Realm capabilities, but unfortunately their behavior had diverged slightly, which caused problems with RealmBaseAdapter.

In order to provide a more consistent experience, we are now introducing two new interfaces: RealmCollection and OrderedRealmCollection.

This provides a solid foundation for adding future collections as well as unifying the behavior and naming of methods across the API.

It has a few implications:

The behavior of methods like remove and clear now only operate on the collection and not the underlying Realm data. This especially impacts RealmResults where these methods will now throw UnsupportedOperationException.

  • RealmBaseAdapter now works out of the box with both RealmList and RealmResults.

  • Methods for deleting objects from both the collection and Realm are now called deleteFromRealm() or deleteAllFromRealm().

  • Realm.clear(Class) and Realm.clear() have been renamed to Realm.delete(Class) and Realm.deleteAll().

  • RealmObject.removeFromRealm() has been renamed to RealmObject.deleteFromRealm().

Stable iterators

One of the design paradigms of Realm is the concept of auto-updating results. Normally that is a great feature to have, except in one case: Changing objects in a way that would remove them from the RealmResult.

Take the following example:

RealmResults<Person> results = realm.where(Person.class).equalTo("inviteToGoogleIO", false).findAll();

// Try to invite all the people
realm.beginTransaction();
for (Person p : results) {
	p.inviteToGoogleIO(true);
}
realm.commitTransaction();

With a normal collection, you would expect to have all persons invited to Google I/O, but due to RealmResults being live-updated, it would actually only send invites to every other person. The reason is that due to the RealmResults live-updating, it would adjust the size of the collection the moment you set the first boolean field to true. This would in turn cause the iterator’s internal index to point to the next item (which hasn’t been updated), so when you call Iterator.next() or similar, it would skip over an item.

The solution so far has been to iterate backwards as that takes into account the changing size():

for (int i = results.size() - 1; i >=0; i--) {
	results.get(i).inviteToGoogleIO(true);
}

This is however very counter-intuitive. With 0.89, the auto-updating feature of RealmResults have changed slightly so instead of RealmResults being live all the time, they are now only updated on Looper events.

This means that iterators will now work as expected but also introduces a slight chance that you might accidentally access a deleted item.

RealmResults<Person> results = realm.where(Person.class).findAll();
for (Person p : results) {
	p.deleteFromRealm(); // Indirectly delete all items one-by-one.
}

// RealmResults are not updated until next Looper event
// So the RealmResult might now contain deleted objects
Person p = results.get(0); 
p.isValid() == false;

// Deleting the item directly on the RealmResults will remove them
// from the RealmResults as well
results.deleteFromRealm(0);

// and new Queries will also exclude deleted objects
results = realm.where(Person.class).findAll(); // Deleted users are removed

Read more here.

This change also impacts all RealmChangeListeners that were previously triggered after each call to Realm.commmitTransaction() on the same thread. They are now deferred to the next Looper event just like changes from other threads. This should have no effect on normal app behavior, but could potentially cause issues in unit tests that assumed that callbacks where immediately notified.

Breaking changes 🚨

PrimaryKey fields are no longer automatically marked as @Required. They can now be null if the type of the field can usually be null.

This change will throw a RealmMigrationNeededException. Either manually add @Required to the primary field to maintain the same behavior as 0.88.3 and below, or change the nullability in a migration step.

RealmObjectSchema personSchema = schema.get("Person");
personSchema.setNullable("myPrimaryKey", true);

Other improvements

See the full changelog for all the details.


Thanks for reading. Now go forth and build amazing apps with Realm! As always, we’re around on Stack Overflow, GitHub, and Twitter.


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.

Get more development news like this