Simplified Realm usage with Monarchy (based on Android Architecture Components and LiveData)

Spoilers: our Realm query will look like this, and it’ll make our life much easier:

See the full sample app for Monarchy here.

Specifically, this means that now we can easily use Realm as our local datasource implementation in a reactive data layer, without exposing any Realm instances and RealmResults outside of our Repository. If this intrigues you, read on further.

— — — — — — — — — — — — — — — — — — — — —

I must admit: I like Realm.

I really do!

Realm’s design principles encourage many great practices that have simplified Android development. When you use Realm, or when you look at the newer Android Architecture Components — specifically LiveData, and the way Room provides LiveData<List<T>> — you can see that Realm has influenced and even shaped the design of how the data layer of a modern Android application works.

Instead of manually fetching data with callbacks, and managing invalidation by hand — we can just start observing data sets.

When you see a RealmResults , it is a collection that can be observed for changes. This RealmResults is associated with a query. When a change occurs, this query is re-evaluated; and if the collection has changed, the listener is called.

When you look at a query definition in Room’s DAO classes, you see

In principle, we see the exact same thing: we create a query definition that the underlying database (or an abstraction on top of it) will re-evaluate in case of change.

What we can improve on using Realm

As much as I like Realm, I’ve come to notice a pattern in where developers often seem to mess up — in such a way that Realm itself cannot detect it.

You’d think it’s the threading (how ominous!), but that’s actually easily verified: throws exceptions and all that when it’s misused. If you ask me, I’ll tell you that threading with Realm is easy. Just make sure you get a Realm instance for the thread you’re working on, and don’t pass managed objects/results in-between.

— —

No, the actual hard part is manual ref-count and lifecycle management. Having to open a Realm instance with getInstance(), retain that instance by passing it between methods (because any call to getInstance() increments said ref-count), and close it with close() when it is no longer used afterwards.

This means that a method that works with Realm should either manage the lifetime of Realm internally, or have Realm passed to it. Or you could keep your own ThreadLocal cache (but that’s rather uncommon).

Instead, how often have you seen some strange MVP implementation with the Presenter having a closeRealm() method, that clearly shouldn’t belong to it?

— —

To solve this problem, we must make sure that the reference counting of the thread-local Realm instances is automatic, but safe.

The benefits of LiveData, and how it can help us out

LiveData is an amazing construct. It is currently the best addition from the AAC, by far — it’ll only be topped by the Paging library. LiveData solves the issues of synchronization when you’re trying to post a value from a background thread to the UI thread.

You can also subscribe to a LiveData (and observe it), receive callbacks to when you’ve actually got subscribers or not ( onActive and onInactive), and in most cases it can even handle automatic unsubscription when it is no longer needed (because its enclosing LifecycleOwner is dead).

So what do we have from LiveData, again?

  • Automatic lifecycle management, unsubscription when no longer needed
  • Ability to handle reference counting based on callbacks
  • Safely passing (immutable) data from any background thread to UI thread

And what was our “problem” with Realm? Manual lifecycle management, and manual reference counting, right?

We can use LiveData to manage the Realm lifecycle.

Introducing Monarchy

With that in mind, I’ve published a new library called Monarchy.

Monarchy is intended to be a singleton for a given RealmConfiguration, and allows you to expose your Realm query results as LiveData, which manage their own Realm instances internally, based on lifecycle of the LifecycleOwner, and whether there are active listeners.

In simple terms, it allows you to listen to RealmResults on a background looper thread, automatically manage Realm instances, and therefore allow you to easily wrap Realm in your data layer — which is generally a must for followers of Clean Architecture.

Here’s some example code:

If we just want detached objects and not mapped ones, then we can use

realmDogs = monarchy.findAllCopiedWithChanges(
realm -> realm.where(RealmDog.class));
// this uses realm.copyFromRealm() to detach objects

To use it with RxJava, then as with any LiveData, this can be exposed as a Flowable.

this.flowable = Flowable.fromPublisher(
LiveDataReactiveStreams.toPublisher(this, liveData));

And if we still want to keep a UI-thread Realm instance with managed objects (to leverage the lazy evaluation that Realm gives us), then the code is similar:

Considering the data is exposed as LiveData, we can use either version as a local data source implementation, following the new Android Architecture Components guidelines, and the Repository pattern (which exposes LiveData, and not just Single or callbacks).

The nice trick here is that findAllMapped() and findAllCopied() both work on a dedicated background looper thread, which means it observes Realm for changes, but also keeps copying the result set from Realm off the UI thread.

Of course, in these cases, the objects are eagerly evaluated.

Some other niceties

To prevent the need for any manual calls to Realm.getDefaultInstance() and Realm.close(), Monarchy provides the following additional methods:

  • doWithRealm((realm) -> { … })
  • runTransactionSync((realm) -> { … })
  • writeAsync((realm) -> { … })

Where the required getInstance() and close() calls are properly called by Monarchy.

Conclusion

If you’ve been using Realm, but had trouble with hiding Realm as an implementation detail of the data layer and keep providing observable collections, then you should definitely give Monarchy a try.

Please note that Monarchy is actually super-new, current version is

implementation 'com.github.Zhuinden:realm-monarchy:0.1.1' 

So if you see anything, I’m available for feedback! But generally you shouldn’t run into any troubles. I expect that you’re using Realm-Java 4.3.1 (which is at this time the current latest).

In the future, when Paging library reaches 1.0, I intend to add findAllPagedWithChanges() method, which will expose RealmResults as a live paged list.

For more info, check out the readme, in Monarchy’s Github repository.