Realm Java 0.84 — with Async Queries & Transactions!

We just released a new version of Realm Java to this website and to Maven. This release includes support for asynchronous queries and asynchronous write transactions!

Android applications are inherently asynchronous, and thus one of the most requested features was the ability to execute Realm read queries or write operations asynchronously on a worker thread.

Asynchronous Queries

You can now execute queries asynchronously on a worker thread.

Async queries are very similar to our existing, synchronous queries. You can keep using the same fluent API to build the query, but the last method in the call will decide the behavior if the RealmQuery is synchronized (findAll()) or asynchronous (findAllAsync()).

Example: finding users with name “John” or “Peter”

First create the query:

RealmResults<User> result = realm.where(User.class)
                              .equalTo("name", "John")
                              .or()
                              .equalTo("name", "Peter")
                              .findAllAsync();

Note that the query is not blocking and immediately returns a RealmResults<User>. This is a promise (similar to the concept of Future in standard Java). The query will continue to run in a background thread, and once it completes it will update the returned instance of RealmResults with the appropriate results.

If you want to be notified of the query completion, you can register a callback. This callback will also be called every time the query is refreshed to reflect the latest changes in Realm (usually after a commit).

private RealmChangeListener callback = new RealmChangeListener() {
    @Override
    public void onChange() { // called once the query complete and on every update
    // use the result
    }
};

public void onStart() {
    RealmResults<User> result = realm.where(User.class).findAllAsync();
    result.addChangeListener(callback);
}

Since we hold a strong reference to the listener, don’t forget to remove any registered listener to avoid leaking the enclosing class.

public void onStop () {
    result.removeChangeListener(callback); // remove a particular listener
    // or 
    result.removeChangeListeners(); // remove all registered listeners
}

At any time you can check if the returned RealmResults has completed or not

result.isLoaded()

Calling isLoaded on a RealmResults obtained synchronously will always return true.

Non-Looper threads caveat: The Async query needs to use the Realm’s Handler in order to deliver results consistently. Trying to call an asynchronous query using a Realm opened inside a thread without a Looper will throw an IllegalStateException

Asynchronous Transactions

You can now also execute write transactions asynchronously on a worker thread.

It works the same way as the current executeTransaction, but instead of opening a Realm on the same thread, it will give you a background Realm opened on a different thread to work with.

You can register a callback if you wish to be notified when the transaction completes or fails (this also requires the Realm’s Handler to deliver the callback. If you start an async write from a Realm opened from a non-Looper thread, you won’t get the notification):

realm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm bgRealm) {
                User user = bgRealm.createObject(User.class);
                user.setName("John");
                user.setEmail("john@corporation.com");
            }
        }, new Realm.Transaction.Callback() {
            @Override
            public void onSuccess() {
            }

            @Override
            public void onError(Exception e) { 
                // transaction is automatically rolled-back, do any cleanup here 
            }
        });

The callback is optional and can be null if you want to fire and forget:

realm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm bgRealm) {
                User user = bgRealm.createObject(User.class);
                user.setName("John");
                user.setEmail("john@corporation.com");
            }
        }, null);

We hold a strong reference to the callback until the write transaction completes. In case you want to cancel a pending transaction (e.g. you’re quitting your Activity/Fragment) don’t forget to call cancel — it will attempt to cancel the scheduled transaction and remove the reference to your callback in order to avoid leaking the instance/enclosing class:

RealmAsyncTask transaction = realm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm bgRealm) {
            }
        }, null);

// configuration change ... 

public void onStop () {
    if (transaction != null && !transaction.isCancelled()) {
        transaction.cancel();
    }
}

New query options

We’ve added two new ways of doing queries: isEmpty() and distinct().

RealmQuery.isEmpty() can be used to test if a value is not null but considered empty, i.e. a RealmList or byte array with 0 elements is empty and the String "" is empty. isEmpty() does not work on number values.

// Find all users with no dogs
RealmResults<User> users = realm.where(User.class).isEmpty("dogs").findAll(); 

// Find all users with at least 1 dog
RealmResults<User> users = realm.where(User.class).not().isEmpty("dogs").findAll();

Realm.distinct() can be used to return a distinct set of elements as defined by a given field. distinct will only work on fields that are indexed (@Index or @PrimaryKey).

// Returns the set of users that all have a different name
RealmResults<User> users = realm.distinct(User.class, "name");

More utility methods

We’ve added some extra utility methods that make it possible to ask about the state of the Realm before interacting with it:

  • Realm.isClosed(): Checks if the underlying Realm file is still open.
  • Realm.isInTransaction(): Checks if the Realm is currently in a write transaction.
  • RealmQuery.isValid(), RealmList.isValid(), RealmResults.isValid(): Checks if the underlying Realm hasn’t been closed or the data deleted.

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 StackOverflow, GitHub, and Twitter.