This is not the current version. View the latest documentation

Realm Java enables you to efficiently write your app’s model layer in a safe, persisted and fast way. Here’s what it looks like:

// Define you model class by extending RealmObject
public class Dog extends RealmObject {
    private String name;
    private int age;

    // ... Generated getters and setters ...
}

public class Person extends RealmObject {
    @PrimaryKey
    private long id;
    private String name;
    private RealmList<Dog> dogs; // Declare one-to-many relationships

    // ... Generated getters and setters ...
}

// Use them like regular java objects
Dog dog = new Dog();
dog.setName("Rex");
dog.setAge(1);

// Initialize Realm
Realm.init(context);

// Get a Realm instance for this thread
Realm realm = Realm.getDefaultInstance();

// Query Realm for all dogs younger than 2 years old
final RealmResults<Dog> puppies = realm.where(Dog.class).lessThan("age", 2).findAll();
puppies.size(); // => 0 because no dogs have been added to the Realm yet

// Persist your data in a transaction
realm.beginTransaction();
final Dog managedDog = realm.copyToRealm(dog); // Persist unmanaged objects
Person person = realm.createObject(Person.class); // Create managed objects directly
person.getDogs().add(managedDog);
realm.commitTransaction();

// Listeners will be notified when data changes
puppies.addChangeListener(new OrderedRealmCollectionChangeListener<RealmResults<Dog>>() {
    @Override
    public void onChange(RealmResults<Dog> results, OrderedCollectionChangeSet changeSet) {
        // Query results are updated in real time with fine grained notifications.
        changeSet.getInsertions(); // => [0] is added.
    }
});

// Asynchronously update objects on a background thread
realm.executeTransactionAsync(new Realm.Transaction() {
    @Override
    public void execute(Realm bgRealm) {
        Dog dog = bgRealm.where(Dog.class).equalTo("age", 1).findFirst();
        dog.setAge(3);
    }
}, new Realm.Transaction.OnSuccess() {
    @Override
    public void onSuccess() {
    	// Original queries and Realm objects are automatically updated.
    	puppies.size(); // => 0 because there are no more puppies younger than 2 years old
    	managedDog.getAge();   // => 3 the dogs age is updated
    }
});

Getting Started

Download Realm for Android

Or, download the source for realm-java on GitHub.

Prerequisites

  • Android Studio version 1.5.1 or higher
  • JDK version 7.0 or higher
  • A recent version of the Android SDK
  • Android API Level 9 or higher (Android 2.3 and above)

Note: Realm does not currently support Java outside of Android. We no longer support Eclipse as an IDE; please migrate to Android Studio.

Installation

Realm is installed as a Gradle plugin.

Step 1: Add the class path dependency to the project level build.gradle file.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath "io.realm:realm-gradle-plugin:3.4.0"
    }
}

The project level build.gradle file is located here:

Project level build.gradle file

Step 2: Apply the realm-android plugin to the top of the application level build.gradle file.

apply plugin: 'realm-android'

The application level build.gradle is located here:

Application level build.gradle file

Once these two changes are made, simply refresh your gradle dependencies. If you are upgrading from a version prior to v0.88 you may also need to clean your gradle project (./gradlew clean) to remove any lingering previous installations.

A sample of the two modified build.gradle files can be found here:

Other Build Systems

The Maven & Ant build systems are not supported. If you wish to see support for these build systems, please express your interest in the below issues:

ProGuard

A ProGuard configuration is provided as part of the Realm library. This means that you don’t need to add any Realm specific rules to your ProGuard configuration.

Realm Browser

We provide a standalone Mac app named Realm Browser to read and edit .realm databases.

Realm Browser

You can generate a test database with sample data using the menu item Tools > Generate demo database.

If you need help finding your app’s Realm file, check this StackOverflow answer for detailed instructions.

The Realm Browser is available on the Mac App Store or you can download it from our GitHub page.

The browser currently doesn’t work on Windows or Linux. On these platforms you can instead use Stetho-Realm. Stetho is an Android debug bridge for the Chrome browser created by Facebook.

API Reference

You can consult our full API reference for all classes, methods & more.

Examples

Take a look at our examples to see Realm used in practice in an app. See here for more details on how to run the examples.

The introExample contains simple examples of how you use the current API.

The gridViewExample is a trivial app that shows how to use Realm as the backing store for a GridView. It also shows how you could populate the database with JSON using GSON plus how to use ABI splits to minimize the size of the final APK.

The threadExample is a simple app that shows how to use Realm in a multithreaded environment.

The adapterExample shows how to use the RealmBaseAdapter and RealmRecyclerViewAdapter to make Realm work with Android ListView and RecyclerView in an elegant way.

The jsonExample illustrates how to use the new Realm JSON facilities.

The encryptionExample shows you how to work with encrypted Realms.

The rxJavaExamples shows how Realm works together with RxJava.

The unitTestExample shows how you can write unit tests when working with Realm.

Getting Help

  • Need help with your code? Ask on StackOverflow. We actively monitor & answer questions on SO!
  • Have a bug to report? Open an issue on our repo. If possible, include the version of Realm, a full log, the Realm file, and a project that shows the issue.
  • Have a feature request? Open an issue on our repo. Tell us what the feature should do, and why you want the feature.
  • Love to follow what comes up next? Look at our changelog. The log shows the latest additions and changes we plan to release soon, and the history of how Realm has evolved.

Models

Realm model classes are created by extending the RealmObject base class.

public class User extends RealmObject {

    private String          name;
    private int             age;

    @Ignore
    private int             sessionId;

    // Standard getters & setters generated by your IDE…
    public String getName() { return name; }
    public void   setName(String name) { this.name = name; }
    public int    getAge() { return age; }
    public void   setAge(int age) { this.age = age; }
    public int    getSessionId() { return sessionId; }
    public void   setSessionId(int sessionId) { this.sessionId = sessionId; }
}

A Realm model class also supports public, protected and private fields as well as custom methods.

public class User extends RealmObject {

    public String name;

    public boolean hasLongName() {
      return name.length() > 7;
    }

    @Override
    public boolean equals(Object o) {
      // Custom equals comparison
    }
}

Field types

Realm supports the following field types: boolean, byte, short, int, long, float, double, String, Date and byte[]. The integer types byte, short, int, and long are all mapped to the same type (long actually) within Realm. Moreover, subclasses of RealmObject and RealmList<? extends RealmObject> are supported to model relationships.

The boxed types Boolean, Byte, Short, Integer, Long, Float and Double can also be used in model classes. Using these types, it is possible to set the value of a field to null.

Required fields and null values

In some cases, null is not an appropriate value of a field. The @Required annotation can be used to tell Realm to enforce checks to disallow null values. Only Boolean, Byte, Short, Integer, Long, Float, Double, String, byte[] and Date can be annotated with Required. Compiling will fail when fields with other types have the @Required annotation. Fields with primitive types and the RealmList type are required implicitly. Fields with RealmObject type are always nullable.

Ignoring properties

The annotation @Ignore implies that a field should not be persisted to disk. Ignored fields are useful if your input contains more fields than your model, and you don’t wish to have many special cases for handling these unused data fields.

static and transient fields are ignored by default and do not need the @Ignore annotation.

Auto-Updating Objects

RealmObjects are live, auto-updating views into the underlying data, which means objects never have to be refreshed. Modifying objects that affect the query will be reflected in the results immediately.

realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        Dog myDog = realm.createObject(Dog.class);
        myDog.setName("Fido");
        myDog.setAge(1);
    }
});
Dog myDog = realm.where(Dog.class).equalTo("age", 1).findFirst();

realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        Dog myPuppy = realm.where(Dog.class).equalTo("age", 1).findFirst();
        myPuppy.setAge(2);
    }
});

myDog.getAge(); // => 2

This property of all RealmObject’s and RealmResults not only keeps Realm fast and efficient, but it allows your code to be simpler and more reactive. For example, if your Activity or Fragment is dependent on a specific RealmObject or RealmResults instance, you don’t need worry about refreshing or re-fetching it before updating the UI.

You can subscribe to Realm notifications to know when Realm data is updated, indicating when your app’s UI should be refreshed.

Indexing properties

The annotation @Index will add a search index to the field. This will make inserts slower and the data file larger but queries will be faster. So it’s recommended to only add index when optimizing specific situations for read performance. We support indexing: String, byte, short, int, long, boolean and Date fields.

Primary keys

To promote a field to primary key, you use the annotation @PrimaryKey, and the field type has to be either string (String) or integer (byte, short, int, or long) and its boxed variants (Byte, Short, Integer, and Long). It is not possible to use multiple fields (compound key) as a primary key. Using a string field as a primary key implies that the field is indexed (the annotation @PrimaryKey implicitly sets the annotation @Index).

Using primary keys makes it possible to use the copyToRealmOrUpdate() method, which will look for an existing object with this primary key, and update it if one is found; if none is found, it will create a new object instead. When calling copyToRealmOrUpdate() on classes without primary keys, an exception will be thrown.

Using primary keys has an effect on the performance. Creating and updating object will be a little slower while querying is expected to be a bit faster. It is hard to give numbers as the changes in performance depend on the size of your dataset.

When calling Realm.createObject(), it will return a new object with all fields set to the default value. In this case, there might be a conflict with an existing object whose primary key field is the default value. To avoid this, it is suggested to create an unmanaged object, set values of the fields, and then copy it to Realm by copyToRealm() method.

final MyObject obj = new MyObject();
obj.setId(42);
obj.setName("Fish");
realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        // This will create a new object in Realm or throw an exception if the
        // object already exists (same primary key)
        // realm.copyToRealm(obj);

        // This will update an existing object with the same primary key
        // or create a new object if an object with no primary key = 42
        realm.copyToRealmOrUpdate(obj);
    }
});

For String (String) and boxed integer (Byte, Short, Integer, and Long), Primary keys can have the value null unless the @PrimaryKey annotation is additionally combined with @Required annotation.

Customizing Objects

It is possible to use RealmObjects almost like POJOs. Extending from RealmObject, you can let the fields be public, and use simple assignments instead of setters and getter. An example of such a model class is:

public class Dog extends RealmObject {
    public String name;
    public int age;
}

You can use Dog like any other class. In order to create a managed Dog object in Realm, you can use the createObject() or copyToRealm() methods.

realm.executeTransaction(new Realm.Transaction() {
    @Overrride
    public void execute(Realm realm) {
        Dog dog = realm.createObject(Dog.class);
        dog.name = "Fido";
        dog.age  = 5;
    }
};

You can add logic to your setters and getters if that fits your needs better. This can be useful if you wish to validate values before storing them in your Realm. Moreover, you can easily add custom methods to your RealmObjects.

RealmModel interface

An alternative to extending the RealmObject base class is implementing the RealmModel interface and adding the @RealmClass annotation.

@RealmClass
public class User implements RealmModel {

}

All methods available on RealmObject are then available through static methods.

// With RealmObject
user.isValid();
user.addChangeListener(listener);

// With RealmModel
RealmObject.isValid(user);
RealmObject.addChangeListener(user, listener);

Relationships

Any two RealmObjects can be linked together.

public class Email extends RealmObject {
    private String address;
    private boolean active;
    // ... setters and getters left out
}

public class Contact extends RealmObject {
    private String name;
    private Email email;
    // ... setters and getters left out
}

Relationships are generally cheap in Realm. This means that following a link is not expensive in terms of speed, and the internal presentation of relationships is highly efficient in terms of memory consumption.

Many-to-One

Simply declare a property with the type of one of you RealmObject subclasses:

public class Contact extends RealmObject {
    private Email email;
    // Other fields…
}

Each contact (instance of Contact) have either 0 or 1 email (instance of Email). In Realm, nothing prevent you from using the same email object in multiple contacts, and the model above can be a many-to-one relationship but often used to model one-to-one relationships.

Setting the RealmObject field to null will clear the reference but the object will not be deleted from the Realm.

Many-to-Many

You can establish a relationship to any number of objects from a single object via a RealmList<T> field declaration. For example, consider a contact with multiple email addresses:

public class Contact extends RealmObject {
    public String name;
    public RealmList<Email> emails;
}

public class Email extends RealmObject {
    public String address;
    public boolean active;
}

RealmLists are basically containers of RealmObjects, and a RealmList behaves very much like a regular Java List. There are no limitations in Realm to use the same object twice (or more) in different RealmLists, and you can use this to model both one-to-many, and many-to-many relationships.

You can create objects, and use RealmList.add() to add the Email objects to the Contact object:

realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        Contact contact = realm.createObject(Contact.class);
        contact.name = "John Doe";

        Email email1 = realm.createObject(Email.class);
        email1.address = "john@example.com";
        email1.active = true;
        contact.emails.add(email1);

        Email email2 = realm.createObject(Email.class);
        email2.address = "jd@example.com";
        email2.active = false;
        contact.emails.add(email2);
    }
});

It is possible to declare recursive relationships which can be useful when modeling certain types of data.

public class Person extends RealmObject {
    public String name;
    public RealmList<Person> friends;
    // Other fields…
}

Setting the value to null for a RealmList field will clear the list. That is, the list will be empty (length zero), but no objects have been deleted. The getter for a RealmList will never return null. The returned object is always a list but the length might be zero.

It is possible to query links or relationships. Consider the model below:

public class Person extends RealmObject {
  private String id;
  private String name;
  private RealmList<Dog> dogs;
  // getters and setters
}

public class Dog extends RealmObject {
  private String id;
  private String name;
  private String color;
  // getters and setters
}

Each Person object has multiple dog relationships as shown in this table diagram:

Table Diagram

Let’s find some persons with link queries …

// persons => [U1,U2]
RealmResults<Person> persons = realm.where(Person.class)
                                .equalTo("dogs.color", "Brown")
                                .findAll();

First of all, notice that the field name in the equalTo condition contains the path through the relationships (separated by period .).

The query above should read, find all Persons who have dogs who are ‘Brown’. It is important to understand that the result will contain the Dog objects which do not fulfill the condition since they are part of the Person’s object:

persons.get(0).getDogs(); // => [A,B]
persons.get(1).getDogs(); // => [B,C,D]

This can be further examined by the following two queries.

// r1 => [U1,U2]
RealmResults<Person> r1 = realm.where(Person.class)
                             .equalTo("dogs.name", "Fluffy")
                             .findAll();

// r2 => [U1,U2]
RealmResults<Person> r2 = r1.where()
                          .equalTo("dogs.color", "Brown")
                          .findAll();

Notice how the first query returned both Person objects because the condition matched both persons. Each Person in the query result contains a list of Dog objects - all of their dog objects (even ones that do not fulfill the original query condition). Remember, we’re searching for people who have particular kinds of dogs (names and colors), not the actual dogs themselves. Therefore, the second query will be evaluated against the first Person query result (r1) and each of the Persons dogs. The second query also matches both persons as well, but this time it’s because of the color of the dog.

Let’s dig a little deeper to help solidify this concept. Please review the following example:

// r1 => [U1,U2]
RealmResults<Person> r1 = realm.where(Person.class)
                             .equalTo("dogs.name", "Fluffy")
                             .equalTo("dogs.color", "Brown")
                             .findAll();

// r2 => [U2]
RealmResults<Person> r2 = realm.where(Person.class)
                             .equalTo("dogs.name", "Fluffy")
                             .findAll()
                             .where()
                             .equalTo("dogs.color", "Brown")
                             .findAll();
                             .where()
                             .equalTo("dogs.color", "Yellow")
                             .findAll();

The first query should read, find all Persons who have dogs named ‘Fluffy’ and also find all Persons who have dogs who are ‘Brown’ and then give me the intersection of the two. The second query should read, find all Persons who have dogs named ‘Fluffy’. Then, given that result set, find all Persons who have dogs whose color is ‘Brown’ and given that result set find all Persons who have dogs whose color is ‘Yellow’.

Let’s take a look at the query behind r1 to fully understand what is happening. The two conditions are equalTo("dogs.name", "Fluffy") and equalTo("dogs.color", "Brown"). The first condition is fulfilled for U1 and U2 - this is set C1. The second condition is also fulfilled for U1 and U2 - this is set C2. The logical-and in the query is the same as an intersection of the two sets C1 and C2. The intersection between C1 and C2 is U1 and U2. Therefore, r1 is U1 and U2.

The query behind r2 is different. Let’s begin by breaking this query apart. The first portion of the query looks like this: RealmResults<Person> r2a = realm.where(Person.class).equalTo("dogs.name", "Fluffy").findAll();. It matches U1 and U2. Then, r2b = r2a.where().equalTo("dogs.color", "Brown").findAll(); also matches U1 and U2 (both persons have brown dogs). The final query, r2 = r2b.where().equalTo("dogs.color", "Yellow").findAll(); matches only U2 since the only person in the brown dog result set that has a Yellow dog is U2.

Inverse relationships

Relationships are unidirectional. Taking the two classes Person and Dog as an example, it means that you can follow the link from a Person object to the Dog objects but there are no way that you can go from a Dog object to its Person objects.

With linking objects fields, you can obtain all objects that link to a given object from a specific field. For example, a Dog object can have a field named owners that contains all of the Person objects that have this exact Dog object in their dogs field. This is done by annotating the owners field with @LinkingObjects and specifying the relationship it has with the Person object.

public class Person extends RealmObject {
  private String id;
  private String name;
  private RealmList<Dog> dogs;
  // getters and setters
}

public class Dog extends RealmObject {
  private String id;
  private String name;
  private String color;
  @LinkingObjects("dogs")
  private final RealmResults<Person> owners;
  // getters and setters
}

The field must be final and the type has to be RealmResults<T> where T is the type/class of opposite end of the relationship. Since relationships are either many-to-one or many-to-many, following inverse relationships can results in 0, 1 or more objects. Like any other RealmResults, it is possible to query an inverse relationship.

Querying inverse relationships

Using inverse relationships, you can expand your query possibilities. Let’s consider the same two model classes, Person and Dog.

Previously, you saw a query for searching persons associated with a brown dog called Fluffy. Instead of starting the query with Person, you can query first for the dog, and follow the inverse relationship to the persons.

RealmResults<Dog> brownFluffies = realm.where(Dog.class).equalTo("color", "Brown").equalTo("name", "Fluffy").findAll();
for (Dog brownFluffy : brownFluffies) {
    RealmResults<Person> owners = brownFluffy.getOwners();
    // ...
}

it is possible to query inverse relationship like a link query. For example, you can find all dogs which are associated with a person called Jane using the following query:

RealmResults<Dog> dogs = realm.where(Dog.class).equalTo("persons.name", "Jane").findAll();
// dogs => [A, B]

Writes

Read operations are implicit which means that objects can be accessed and queried at any time. All write operations (adding, modifying, and removing objects) must be wrapped in write transactions. A write transaction can either be committed or cancelled. During the commit, all changes will be written to disk, and the commit will only succeed if all changes can be persisted. By cancelling a write transaction, all changes will be discarded. Using write transactions, your data will always be in a consistent state.

Write transactions are also used to ensure thread safety:

// Obtain a Realm instance
Realm realm = Realm.getDefaultInstance();

realm.beginTransaction();

//... add or update objects here ...

realm.commitTransaction();

While working with your RealmObjects inside a write transaction, you might end up in a situation where you wish to discard the change. Instead of committing it, and then reverting it, you can simply cancel the write transaction:

realm.beginTransaction();
User user = realm.createObject(User.class);

//  ...

realm.cancelTransaction();

Please note that write transactions block each other. This can cause ANR errors if you are creating write transactions on both the UI and background threads at the same time. To avoid this, use async transactions when creating write transactions on the UI thread.

Realm is crash safe, so in case of an Exception happening inside a transaction, the Realm itself will not be corrupted. Only the data in the current transaction will be lost. In case the Exception is caught and the app continues, it is important to cancel the transaction. This is done automatically if you use executeTransaction().

Thanks to Realm’s MVCC architecture, reads are not blocked while a write transaction is open! This means that unless you need to make simultaneous transactions from many threads at once, you can favor larger transactions that do more work over many fine-grained transactions. When you commit a write transaction to a Realm, all other instances of that Realm will be notified, and be updated automatically.

Read & write access in Realm is ACID.

Creating objects

Because RealmObjects are strongly tied to a Realm, they should be instantiated through the Realm directly:

realm.beginTransaction();
User user = realm.createObject(User.class); // Create a new object
user.setName("John");
user.setEmail("john@corporation.com");
realm.commitTransaction();

Alternatively you can create an instance of an object first and add it later using realm.copyToRealm(). Realm supports as many custom constructors as you like as long as one of them is a public no arguments constructor.

User user = new User("John");
user.setEmail("john@corporation.com");

// Copy the object to Realm. Any further changes must happen on realmUser
realm.beginTransaction();
User realmUser = realm.copyToRealm(user);
realm.commitTransaction();

When using realm.copyToRealm() it is important to remember that only the returned object is managed by Realm, so any further changes to the original object will not be persisted.

Transaction blocks

Instead of manually keeping track of realm.beginTransaction(), realm.commitTransaction(), and realm.cancelTransaction() you can use the realm.executeTransaction() method, which will automatically handle begin/commit, and cancel if an error happens.

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

Asynchronous Transactions

As transactions are blocked by other transactions it can be an advantage to do all writes on a background thread in order to avoid blocking the UI thread. By using an asynchronous transaction, Realm will run that transaction on a background thread and report back when the transaction is done.

realm.executeTransactionAsync(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.OnSuccess() {
            @Override
            public void onSuccess() {
                // Transaction was a success.
            }
        }, new Realm.Transaction.OnError() {
            @Override
            public void onError(Throwable error) {
                // Transaction failed and was automatically canceled.
            }
        });

OnSuccess and OnError callbacks are both optional, but if provided they will be called when the transaction completes successfully or fails, respectively. Callbacks are controlled by the Looper, so they are only allowed on Looper threads.

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

An asynchronous transaction is represented by the RealmAsyncTask object. This object can be used to cancel any pending transaction if you are quitting the Activity/Fragment before the transaction is completed. Forgetting to cancel a transaction can crash the app if the callback updates the UI.

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

Updating strings and byte arrays

Realm is working on entire fields, and it is not possible to update individual elements of strings or byte arrays. Suppose you need to update the 5th element of a string, you will have to do something like

realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        bytes[] bytes = realmObject.binary;
        bytes[4] = 'a';
        realmObject.binary = bytes;
    }
});

This is due to Realm’s MVCC architecture which avoids mutating existing data in place to ensure that other threads or processes reading the data see it in a consistent state.

Queries

All fetches (including queries) are lazy in Realm, and the data is never copied.

Realm’s query engine uses a Fluent interface to construct multi-clause queries.

Using the User class -

public class User extends RealmObject {

    @PrimaryKey
    private String          name;
    private int             age;

    @Ignore
    private int             sessionId;

    // Standard getters & setters generated by your IDE…
    public String getName() { return name; }
    public void   setName(String name) { this.name = name; }
    public int    getAge() { return age; }
    public void   setAge(int age) { this.age = age; }
    public int    getSessionId() { return sessionId; }
    public void   setSessionId(int sessionId) { this.sessionId = sessionId; }
}

To find all users named John or Peter you would write:

// Build the query looking at all users:
RealmQuery<User> query = realm.where(User.class);

// Add query conditions:
query.equalTo("name", "John");
query.or().equalTo("name", "Peter");

// Execute the query:
RealmResults<User> result1 = query.findAll();

// Or alternatively do the same all at once (the "Fluent interface"):
RealmResults<User> result2 = realm.where(User.class)
                                  .equalTo("name", "John")
                                  .or()
                                  .equalTo("name", "Peter")
                                  .findAll();

This gives you a new instance of the class RealmResults, containing the users with the name John or Peter. The query is executed when the method findAll() is called. The method is member of a family of findAll methods, for example findAllSorted() will returned a sort result set, and findAllAsync() will perform the query asynchronously on a background thread. Please dive into the API reference to learn all the details.

Objects are not copied - you get a list of references to the matching objects, and you work directly with the original objects that matches your query. The RealmResults inherits from Java’s AbstractList, and behaves in similar ways. For example, RealmResults are ordered, and you can access the individual objects through an index.

When a query does not have any matches, the returned RealmResults object will not be null, but the size() method will return 0.

If you wish to modify or delete objects in a RealmResults set, you must do so in a write transaction.

You can also query relationships.

Filtering

With the where() method you initiate a query represented by a RealmQuery object. Once you have a RealmQuery, you can use various predicates to filter the data returned by the query. Most predicates have self-explanatory names.

The first argument of a predicate is a field name. If a predicate isn’t supported by the field’s type, an exception is thrown. Please consult the API reference for RealmQuery for details.

For all data types, you have the following predicates:

  • equalTo()
  • notEqualTo()
  • in()

To match a field against a list of values, use in(). For example, to find the names “Jill,” “William,” or “Trillian”, you can use in("name", new String[]{"Jill", "William", "Trillian"}). The in() predicate is applicable to strings, binary data, and numeric fields (including dates).

Numeric data types, including Date, allow these additional predicates:

  • between() (Both end points are included, i.e., it is a bounded interval)
  • greaterThan()
  • lessThan()
  • greaterThanOrEqualTo()
  • lessThanOrEqualTo()

String fields allow these additional predicates:

  • contains()
  • beginsWith()
  • endsWith()
  • like()

All four string predicates have an optional third argument to control case sensitivity. Set it to Case.INSENSITIVE to ignore case while matching, or Case.SENSITIVE to perform case-sensitive matching. The default is Case.SENSITIVE.

The predicate like() performs glob-style wildcard matching. The matching pattern consists of characters and one or more wildcards:

  • * matches 0 or more Unicode characters
  • ? matches a single Unicode character

For example, consider a Realm with four objects with a field called name which has the values William, Bill, Jill, and Trillian. The predicate like("name", "?ill*") will match the first three objects, and like("name", "*ia?") will match the first and the last object.

Binary data, strings, and lists of RealmObjects (RealmList) may be empty, i.e., have a length of zero. There are predicates to check for emptiness:

  • isEmpty()
  • isNotEmpty()

If a field is not required, the value can have the value null (recall that fields which are RealmObjects can never be required, and the value can be null). Two useful predicates for matching the null value are:

  • isNull()
  • isNotNull()

Logical Operators

Each condition is implicitly joined with logical AND. Logical OR joins must be applied explicitly using or().

public class User extends RealmObject {

    @PrimaryKey
    private String          name;
    private int             age;

    @Ignore
    private int             sessionId;

    // Standard getters & setters generated by your IDE…
    public String getName() { return name; }
    public void   setName(String name) { this.name = name; }
    public int    getAge() { return age; }
    public void   setAge(int age) { this.age = age; }
    public int    getSessionId() { return sessionId; }
    public void   setSessionId(int sessionId) { this.sessionId = sessionId; }
}

You can also group conditions using beginGroup() and endGroup() to specify order of evaluation:

RealmResults<User> r = realm.where(User.class)
                            .greaterThan("age", 10)  // implicit AND
                            .beginGroup()
                                .equalTo("name", "Peter")
                                .or()
                                .contains("name", "Jo")
                            .endGroup()
                            .findAll();

Conditions may be negated with not(). The not() operator can be used with beginGroup()/endGroup() to negate sub-conditions only. Say you want to all find Users who are not named “Peter” or “Jo.” The query could be:

RealmResults<User> r = realm.where(User.class)
                           .not()
                           .beginGroup()
                                .equalTo("name", "Peter")
                                .or()
                                .contains("name", "Jo")
                            .endGroup()
                            .findAll();

Of course, with this particular query, it is probably easier to use in():

RealmResults<User> r = realm.where(User.class)
                           .not()
                           .in("name", new String[]{"Peter", "Jo"})
                           .findAll();

Sorting

Once you have done your query, you can sort the results like this:

RealmResults<User> result = realm.where(User.class).findAll();
result = result.sort("age"); // Sort ascending
result = result.sort("age", Sort.DESCENDING);

Sorts are ascending by default; to change that, use Sort.DESCENDING as the second optional argument. It is possible to sort using multiple fields simultaneously.

Unique values

Use the distinct() predicate to return only unique values. For example, to find out how many different names you have in your Realm:

RealmResults<Person> unique = realm.where(Person.class).distinct("name");

Only integer and string fields are supported; an exception will be thrown if you try to use distinct() with an unsupported field type. As with sorting, you can specify multiple fields.

Chaining Queries

Since results are never copied and computed on request, you can very efficiently chain queries to gradually filter your data:

RealmResults<Person> teenagers = realm.where(Person.class).between("age", 13, 20).findAll();
Person firstJohn = teenagers.where().equalTo("name", "John").findFirst();

You can also perform chained queries on child objects as well. Assume the above Person object has a list of Dog objects.

public class Dog extends RealmObject {
    private int age;
    // getters & setters ...
}

public class Person extends RealmObject {
    private int age;
    private RealmList<Dog> dogs;
    // getters & setters ...
}

You can query for all people who are between the age of 13 and 20 who have at least one dog which is 1 year old:

RealmResults<Person> teensWithPups = realm.where(Person.class).between("age", 13, 20).equalTo("dogs.age", 1).findAll();

Note that query chains are built on RealmResults, not RealmQuery. If you add more conditions to an existing RealmQuery, then you are modifying the query, not the chains. Read more about link queries.

Auto-Updating Results

RealmResults are live, auto-updating views into the underlying data, which means results never have to be re-fetched. Modifying objects that affect the query will be reflected in the RealmResults immediately.

final RealmResults<Dog> puppies = realm.where(Dog.class).lessThan("age", 2).findAll();
puppies.size(); // => 0

realm.executeTransaction(new Realm.Transaction() {
    @Override
    void public execute(Realm realm) {
        Dog dog = realm.createObject(Dog.class);
        dog.setName("Fido");
        dog.setAge(1);
    }
});

puppies.addChangeListener(new RealmChangeListener() {
    @Override
    public void onChange(RealmResults<Dog> results) {
      // results and puppies point are both up to date
      results.size(); // => 1
      puppies.size(); // => 1
    }
});

This applies to all RealmResults: all objects, filtered and chained.

This property of RealmResults not only keeps Realm fast and efficient, but it allows your code to be simpler and more reactive. For example, if your Activity or Fragment relies on the results of a query, you can store the Realm object or RealmResults in a field and access it without having to make sure to refresh its data prior to each access.

You can subscribe to Realm notifications to know when Realm data is updated, indicating when your app’s UI should be refreshed, without having to re-fetch your RealmResults.

Since results are auto-updating, it’s important to not rely on indices & counts staying constant.

Retrieving objects by type

The most basic method for retrieving objects from a Realm is realm.where(Foo.class).findAll(), which returns a RealmResults<Foo> which contains all instances of the model class being queried.

There are specialized versions of findAll() which offer sorting functionality i.e., you can specify sorting order per field. See realm.where(Foo.class).findAllSorted() for details.

Aggregation

A RealmResults also has various aggregation methods:

RealmResults<User> results = realm.where(User.class).findAll();
long   sum     = results.sum("age").longValue();
long   min     = results.min("age").longValue();
long   max     = results.max("age").longValue();
double average = results.average("age");

long   matches = results.size();

Iterations & Snapshots

All Realm collections are live. This means that they always reflect the latest state. In most cases this is desirable, but what if you’re iterating over a collection with the purpose of modifying the elements? For example:

RealmResults<Person> guests = realm.where(Person.class).equalTo("invited", false).findAll();
realm.beginTransaction();
for (int i = 0; guests.size(); i++) {
    guests.get(i).setInvited(true);
}
realm.commitTransaction();

Typically you would expect this simple loop to invite all guests. Because the RealmResults is updated immediately, though, only half the guests end up being invited! An invited guest is removed immediately from the collection, which will shift all elements. When the i parameter gets incremented, it will miss an element.

To prevent this, you can take a snapshot of a collection’s data. A snapshot guarantees the order of elements will not change, even if an element is deleted or modified.

Iterators created from Realm collections will use a snapshot automatically. RealmResults and RealmList have a createSnapshot() method to create one manually.

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

// Use an iterator to invite all guests
realm.beginTransaction();
for (Person guest : guests) {
    guest.setInvited(true);
}
realm.commitTransaction();

// Use a snapshot to invite all guests
realm.beginTransaction();
OrderedRealmCollectionSnapshot<Person> guestsSnapshot = guests.createSnapshot();
for (int i = 0; guestsSnapshot.size(); i++) {
    guestsSnapshot.get(i).setInvited(true);
}
realm.commitTransaction();

Deletion

You can delete the results of a query from the Realm:

// obtain the results of a query
final RealmResults<Dog> results = realm.where(Dog.class).findAll();

// All changes to data must happen in a transaction
realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        // remove single match
        results.deleteFirstFromRealm();
        results.deleteLastFromRealm();

        // remove a single object
        Dog dog = results.get(5);
        dog.deleteFromRealm();

        // Delete all matches
        results.deleteAllFromRealm();
    }
});

Asynchronous Queries

Queries can be performed on a background thread.

Most queries in Realm are fast enough to be run synchronously - even on the UI thread. However for either very complex queries or queries on large data sets it can be an advantage to run the query on a background thread.

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

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.

If you want to be notified when the query completes and the RealmResults object is updated, you can register a RealmChangeListener. This listener will be called every time the RealmResults are updated to reflect the latest changes in the Realm (usually after a commit).

Register a callback

private OrderedRealmCollectionChangeListener<RealmResults<User> callback = new OrderedRealmCollectionChangeListener<>() {
    @Override
    public void onChange(RealmResults<User> results, OrderedCollectionChangeSet changeSet) {
        if (changeSet == null) {
            // The first time async returns with an null changeSet.
        } else {
            // Called on every update.
        }
    }
};

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

Remember to unregister any listeners when exiting an Activity or Fragment to avoid memory leaks.

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

Check if the query has completed

RealmResults<User> result = realm.where(User.class).findAllAsync();
if (result.isLoaded()) {
  // Results are now available
}

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

Force load an asynchronous query

Optionally you can wait until the query completes. This will block the current thread, making the query synchronous again (same concept as in Future.get()).

RealmResults<User> result = realm.where(User.class).findAllAsync();
result.load() // be careful, this will block the current thread until it returns

Non-Looper threads

You can only use asynchronous queries on a Looper thread. The asynchronous 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.

Realms

Realms are our equivalent of a database: they contain different kinds of objects, and map to one file on disk. Before you can use Realm in your app, you must initialize it first. It is done as follows:

Realm.init(context);

The initialization is done once, and your must provide an Android context. A good place to initialize Realm, is in onCreate() on an application subclass:

public class MyApplication extends Application {
  @Override
  public void onCreate() {
    super.onCreate();
    Realm.init(this);
  }
}

Moreover, you might also wish to add MyApplication to the app’s AndroidManifest.xml:

<application
  android:name=".MyApplication"
  ...
/>

Configuring a Realm

RealmConfiguration object is used to control all aspects of how a Realm is created. The minimal configuration usable by Realm is:

RealmConfiguration config = new RealmConfiguration.Builder().build();

The above configuration will point to a file called default.realm located in Context.getFilesDir().

A typical configuration would be something like below:

// The RealmConfiguration is created using the builder pattern.
// The Realm file will be located in Context.getFilesDir() with name "myrealm.realm"
RealmConfiguration config = new RealmConfiguration.Builder()
  .name("myrealm.realm")
  .encryptionKey(getKey())
  .schemaVersion(42)
  .modules(new MySchemaModule())
  .migration(new MyMigration())
  .build();
// Use the config
Realm realm = Realm.getInstance(config);

It is also possible to have multiple RealmConfigurations. In this way you can control the version, schema and location of each Realm independently.

RealmConfiguration myConfig = new RealmConfiguration.Builder()
  .name("myrealm.realm")
  .schemaVersion(2)
  .modules(new MyCustomSchema())
  .build();

RealmConfiguration otherConfig = new RealmConfiguration.Builder()
  .name("otherrealm.realm")
  .schemaVersion(5)
  .modules(new MyOtherSchema())
  .build();

Realm myRealm = Realm.getInstance(myConfig);
Realm otherRealm = Realm.getInstance(otherConfig);

It is always possible to obtain the absolute path of a Realm by using the Realm.getPath() method.

It is important to note that Realm instances are thread singletons, meaning that the static constructor will return the same instance for every thread.

The Default RealmConfiguration

The RealmConfiguration can be saved as a default configuration. Setting a default configuration in your custom Application class, will ensure that it is available in the rest of your code.

public class MyApplication extends Application {
  @Override
  public void onCreate() {
    super.onCreate();
    // The Realm file will be located in Context.getFilesDir() with name "default.realm"
    Realm.init(this);
    RealmConfiguration config = new RealmConfiguration.Builder().build();
    Realm.setDefaultConfiguration(config);
  }
}

public class MyActivity extends Activity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Realm realm = Realm.getDefaultInstance();
    try {
      // ... Do something ...
    } finally {
      realm.close();
    }
  }
}

Read-only Realms

readOnly() is only enforced in the current process. It is still possible for other processes or devices. Also, any write transaction against a read-only Realm will throw an IllegalStateException. This also includes trying to write the schema, so that must be provided initially by some other source.

It’s sometimes useful to ship a pre-existing Realm file with your app. For example, in addition to the user’s Realm, you may want to bundle some shared data with your application. In many cases you don’t want to accidentially modify that Realm as the data is purely read-only. This can be archived by bundling a Realm file in assets and use the following configuration:

RealmConfiguration config = new RealmConfiguration.Builder()
    .assetFile("my.realm")
    .readOnly()
    // It is optional, but recommended to create a module that describes the classes
    // found in your bundled file. Otherwise if your app contains other classes
    // than those found in the file, it will crash when opening the Realm as the
    // schema cannot be updated in read-only mode.
    .modules(new BundledRealmModule())
    .build();

For synchronized Realms you might want to expose a read-only version of a remote Realm. In those cases you can do it the following way:

SyncUser user = getUser();
String url = getUrl();
SyncConfiguration config = new SyncConfiguration.Builder(user, url)
    // Similar to `assetFile` for local Realms, but it will fetch all remote
    // data before from the server prior to opening the Realm.
    .waitForRemoteInitialData();
    .readOnly()
    .modules(new BundledRealmModule())
    .build();

RealmAsyncTask task = Realm.getInstanceAsync(config, new Realm.Callback() {
    @Override
    public void onSuccess(Realm realm) {
        // Realm is now downloaded and ready. It is readonly locally but will
        // still see new changes coming from the server.
    }
});

In-Memory Realm

Define an instance for an un-persisted in-memory Realm:

RealmConfiguration myConfig = new RealmConfiguration.Builder()
    .name("myrealm.realm")
    .inMemory()
    .build();

Setting this will create an in-memory Realm instead of saving it to disk. In-memory Realms might still use disk space if memory is running low, but all files created by an in-memory Realm will be deleted when the Realm is closed.

Please note that creating an in-memory Realm with the same name as a regular (persisted) Realm is not allowed.

When all in-memory Realm instances with a particular name go out of scope with no references, all data is freed for that Realm. It is recommended that you hold onto a reference to any created in-memory Realms for the duration of your App.

Dynamic Realms

When working with a conventional Realm, the model class is defined using RealmObject subclasses. This has a lot of benefits with regards to type safety. In some cases these types are not available at compile time e.g., during migrations or when working with string based data like CSV files.

A DynamicRealm is a variant of the conventional Realm that makes it possible to work with Realm data without using RealmObject subclasses. Instead all access are done using Strings instead of Classes.

Dynamic Realms are opened using the same configuration as conventional Realms, but they ignore any configured schema, migration, and schema version.

RealmConfiguration realmConfig = new RealmConfiguration.Builder().build();
DynamicRealm realm = DynamicRealm.getInstance(realmConfig);

// In a DynamicRealm all objects are DynamicRealmObjects
DynamicRealmObject person = realm.createObject("Person");

// All fields are accessed using strings
String name = person.getString("name");
int age = person.getInt("age");

// An underlying schema still exists, so accessing a field that does not exist
// will throw an exception
person.getString("I don't exist");

// Queries still work normally
RealmResults<DynamicRealmObject> persons = realm.where("Person")
    .equalTo("name", "John")
    .findAll();

A DynamicRealm trades type safety and performance for flexibility, so only use it in cases where you really need the flexibility.

Asynchronously Opening Realms

Realm.getInstanceAsync(RealmConfiguration, Realm.Callback) will only work from Looper threads.

If opening a Realm might require a time-consuming operation, such as applying migrations, copying files from assets, compaction or downloading the remote contents of a synchronized Realm, you can use the Realm.getInstanceAsync() API to perform all work needed to get the Realm to a usable state on a background thread before opening it on the calling thread. For example:

RealmConfiguration config = new RealmConfiguration.Builder()
    .schema(42)
    .migration(new MyMigration()) // Potentially lengthy migration
    .build();

RealmAsyncTask task = Realm.getInstanceAsync(config, new Realm.Callback() {
    @Override
    public void onSuccess(Realm realm) {
        // Realm is opened and ready on the caller thread.
    }
});

Closing Realm instances

Realm implements Closeable in order to take care of native memory deallocation and file descriptors so it is important to remember to close your Realm instances when you are done with them.

Realm instances are reference counted, which means that if you call getInstance() twice in a thread, you will also have to call close() twice as well. This allows you to implement Runnable classes without having to worry in what thread they will be executed: simply start it with a getInstance() and end it with a close() and you are good to go!

For the UI thread the easiest way is to execute realm.close() in the onDestroy() method.

If you need to create another Looper thread other than the UI one you can use this pattern:

public class MyThread extends Thread {

    private Realm realm;

    @Override
    public void run() {
        Looper.prepare();
        realm = Realm.getDefaultInstance();
        try {
            //... Setup the handlers using the Realm instance ...
            Lopper.loop();
        } finally {
            realm.close();
        }
    }
}

For AsyncTask this is a good pattern:

protected Void doInBackground(Void... params) {
    Realm realm = Realm.getDefaultInstance();
    try {
        // ... Use the Realm instance ...
    } finally {
        realm.close();
    }

    return null;
}

If you are using Thread or Runnable for short-lived tasks, the follow pattern is recommended:

// Run a non-Looper thread with a Realm instance.
Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        Realm realm = Realm.getDefaultInstance();
        try {
            // ... Use the Realm instance ...
        } finally {
            realm.close();
        }
    }
});

thread.start();

If you have the luck to work on an app with minSdkVersion >= 19 and Java >= 7 then you can use try-with-resources:

try (Realm realm = Realm.getDefaultInstance()) {
    // No need to close the Realm instance manually
}

Auto-Refresh

If a Realm instance has been obtained from a thread that is associated with a Looper (the UI thread is by default) then the Realm instance comes with an auto-refresh feature. This means that the Realm instance will be automatically updated to the latest version on every occurrence of the event loop. This is a handy feature that allows you to keep your UI constantly updated with the latest content with very little effort.

If you get a Realm instance from a thread that does not have a Looper attached, then objects from such instance will not be updated unless the waitForChange() method is called. It is important to note that having to hold on to an old version of your data is expensive in terms of memory and disk space and the cost increases with the number of versions between the one being retained and the latest. This is why it is important to close the Realm instance as soon as you are done with it in the thread.

If you want to be sure whether your Realm instance has auto-refresh activated or not you can use the isAutoRefresh() method.

Finding a Realm File

If you need help finding your app’s Realm file, check this StackOverflow answer for detailed instructions.

Threading

There’s actually very little you need to know, or do, when working with various threads with Realm. The key takeaway here is that Realm makes it effortless to work with data on multiple threads without having to worry about consistency or performance because objects and queries are auto-updating at all times.

You can operate on live objects in different threads, reading and writing to them, without worrying about what other threads are doing to those same objects. If you need to change data you can use a transaction. The other objects in the other threads will be updated in near real time (the updates will be scheduled as an event on the Looper, so Looper threads will be updated as soon as the event is processed).

The only limitation is that you cannot randomly pass Realm objects between threads. If you need the same data on another thread you just need to query for that data on the that other thread. Furthermore, you can observe the changes using Realms reactive architecture. Remember - all objects are kept up to date between threads - Realm will notify you when the data changes.

Check out the example below.

Realm Threading Example

Assume we have an app that displays a list of customers. In a background thread (an Android IntentService) we poll a remote endpoint for new customers and then save them to Realm. When the background thread adds new customers, the data in the UI thread will be automatically updated. The UI thread gets notified via a RealmChangeListener and at that point we tell the UI widget to update itself. No need to re-query because Realm keeps everything up to date.

// in a Fragment or Activity, etc
@Override
public void onActivityCreated(Bundle savedInstanceState) {
    // ... boilerplate omitted for brevity
    realm = Realm.getDefaultInstance();
    // get all the customers
    RealmResults<Customer> customers = realm.where(Customer.class).findAllAsync();
    // ... build a list adapter and set it to the ListView/RecyclerView/etc

    // set up a Realm change listener
    changeListener = new RealmChangeListener() {
        @Override
        public void onChange(RealmResults<Customer> results) {
            // This is called anytime the Realm database changes on any thread.
            // Please note, change listeners only work on Looper threads.
            // For non-looper threads, you manually have to use Realm.waitForChange() instead.
            listAdapter.notifyDataSetChanged(); // Update the UI
        }
    };
    // Tell Realm to notify our listener when the customers results
    // have changed (items added, removed, updated, anything of the sort).
    customers.addChangeListener(changeListener);
}

// In a background service, in another thread
public class PollingService extends IntentService {
    @Override
    public void onHandleIntent(Intent intent) {
        Realm realm = Realm.getDefaultInstance();
        try {
            // go do some network calls/etc and get some data and stuff it into a 'json' var
            String json = customerApi.getCustomers();
            realm.beginTransaction();
            realm.createObjectFromJson(Customer.class, json); // Save a bunch of new Customer objects
            realm.commitTransaction();
            // At this point, the data in the UI thread is already up to date.
            // ...
        } finally {
            realm.close();
        }
    }
    // ...
}

Once the background service adds new customers to the UI, the customers list is automatically updated in the UI without any additional intervention on your part. The same goes for individual objects. Suppose that you’re only managing one object. Just change it on one thread and the UI thread automatically has the new data. If you need to respond to that change just add a listener like we’re doing above.

That’s all there is to it.

Using a Realm across Threads

The only rule to using Realm across threads is to remember that Realm, RealmObject or RealmResults instances cannot be passed across threads. However, you can use an asynchronous query or asynchronous transaction, to offload the operation to a background thread and bring any results back to the original thread for you.

When you want to access the same data from a different thread, you can obtain a new Realm instance (i.e. Realm.getInstance(RealmConfiguration config) or its cousins) and get your objects through a query.

The objects will map to the same data on disk, and will be readable & writeable from any thread.

Schemas

The default schema for a Realm is defined as all the Realm model classes in a project. It is possible to change this behavior, e.g. if you want to restrict a Realm to only contain a subset of classes. This is done by creating a custom RealmModule.

// Create the module
@RealmModule(classes = { Person.class, Dog.class })
public class MyModule {
}

// Set the module in the RealmConfiguration to allow only classes defined by the module.
RealmConfiguration config = new RealmConfiguration.Builder()
  .modules(new MyModule())
  .build();

// It is possible to combine multiple modules to one schema.
RealmConfiguration config = new RealmConfiguration.Builder()
  .modules(new MyModule(), new MyOtherModule())
  .build();

Sharing schemas

For library developers: Libraries that include Realm must expose and use their schema through a RealmModule.

Doing so prevents the default RealmModule from being generated for the library project, which would otherwise conflict with the default RealmModule being generated by the app. The library’s RealmModule is also how the library exposes its Realm classes to the app.

// Library must create a module and set library = true. This will prevent the default
// module from being created.
// allClasses = true can be used instead of listing all classes in the library.
@RealmModule(library = true, allClasses = true)
public class MyLibraryModule {
}

// Library projects are therefore required to explicitly set their own module.
RealmConfiguration libraryConfig = new RealmConfiguration.Builder()
  .name("library.realm")
  .modules(new MyLibraryModule())
  .build();

// Apps can add the library RealmModule to their own schema.
RealmConfiguration config = new RealmConfiguration.Builder()
  .name("app.realm")
  .modules(Realm.getDefaultModule(), new MyLibraryModule())
  .build();

It is currently not possible to have multiple RealmModule declarations in a single file. If you have two or more RealmModules, you will have to split the declarations into several files with exactly one declaration per file.

See a complete example of how RealmModules work between library and app projects here.

JSON

It is possible to add RealmObjects represented as JSON directly to Realm whether they are represented as a String, a JSONObject or an InputStream. Realm will ignore any properties in the JSON not defined by the RealmObject. Single object is added through Realm.createObjectFromJson() while lists of objects are added using Realm.createAllFromJson().

// A RealmObject that represents a city
public class City extends RealmObject {
    private String city;
    private int id;
    // getters and setters left out ...
}

// Insert from a string
realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        realm.createObjectFromJson(City.class, "{ city: \"Copenhagen\", id: 1 }");
    }
});

// Insert multiple items using an InputStream
realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        try {
            InputStream is = new FileInputStream(new File("path_to_file"));
            realm.createAllFromJson(City.class, is);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
});

Parsing JSON with Realm is subject to the following rules.

  • Creating object with JSON which has the field with a null value:
    • For a not-required field, set it to null which is the default value.
    • For a required field, throw an exception.
  • Updating object with JSON which has the field with a null value:
    • For a not-required field, set it to null.
    • For a required field, throw an exception.
  • JSON doesn’t have the field:
    • Leave the value unchanged for both required and not-required fields.

Notifications

It is possible to register a listener to be notified whenever a Realm, RealmResults or RealmList is updated by calling the addChangeListener method.

The notification delivery can be stopped by calling the removeChangeListener() or removeAllChangeListeners() method. The notification delivery will also be stopped if the object which the listener was registered on is GCed or the correspoding Realm instance is closed. You should keep a strong reference of the object which the listener is registered on as long as you still need the notification to be delivered.

How Notifications are Delivered

Notifications are always delivered on the thread that they were originally registered on. That thread must have a running Looper.

If the relevant write transaction happened on a different thread, notification listener is asynchronously called after the transaction is committed.

If the relevant write transaction happened on the same thread, notification listener will be called synchronously when the transaction is committed. Also, the listener may be called synchronously when write transaction begins. This will happen if the Realm was advanced to the latest version, Realm entities being observed were modified or deleted in a way that would trigger notifications. Such listeners will run within the context of the current write transaction, meaning attempting to begin a write transaction within the notification handler will cause Realm to throw an exception. If your app is architected in such a way that this scenario could occur, you can use Realm.isInTransaction() to determine whether you are already inside a write transaction or not.

Since asynchronous notifications are delivered through looper events, the delivery of notifications might be delayed by other events in the looper queue. When notifications can’t be delivered immediately, changes from multiple write transactions may be coalesced into a single notification.

Realm Notifications

Your UI or other looper threads can get notified of changes in a Realm by adding a listener, which is executed whenever the Realm is changed:

public class MyActivity extends Activity {
    private Realm realm;
    private RealmChangeListener realmListener;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      realm = Realm.getDefaultInstance();
      realmListener = new RealmChangeListener() {
        @Override
        public void onChange(Realm realm) {
            // ... do something with the updates (UI, etc.) ...
        }};
      realm.addChangeListener(realmListener);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // Remove the listener.
        realm.removeChangeListener(realmListener);
        // Close the Realm instance.
        realm.close();
    }
}

Collection Notifications

Collection notifications are different from Realm change notifications in that they contain information that describe what changes have occurred at a fine-grained level. This consists of the indices of objects that have been inserted, deleted, or modified since the last notification.

Collection notifications are delivered asynchronously, first with the initial results, and then again after each write transaction which changes either any of the objects in the collection, or which objects are in the collection.

These changes can be accessed via the OrderedCollectionChangeSet parameter that is passed to the change listener. This object holds information about the indices affected by deletions, insertions and changes.

The former two, deletions and insertions, record the indices whenever objects start and stop being part of the collection. This takes into account when you add objects to the Realm or delete them from the Realm. For RealmResults this also applies when you filter for specific values and the object was changed so that it is now matching the query or not matching anymore.

You’re notified about changes whenever a field of an object has changed, which was previously part of the collection and is still part of it. This happens as well when to-one and to-many relationships change.

public class Dog extends RealmObject {
  public String name;
  public int age;
}

public class Person exteds RealmObject {
  public String name;
  public RealmList<Dog> dogs;
}

Let’s assume you’re observing a list of dog owners as given by the model code above. You will be notified about modifications for a matched Person object for example when:

  • You modify the Person’s name.
  • You add or remove a Dog from the list of dogs belonging to a Person.
  • You modify the age of a Dog belonging to that Person.

This makes it possible to discretely control animations and visual updates made to the content inside your UI, instead of arbitrarily reloading everything each time a notification occurs.

private final OrderedRealmCollectionChangeListener<RealmResults<Person>> changeListener = new OrderedRealmCollectionChangeListener<>() {
    @Override
    public void onChange(RealmResults<Person> collection, OrderedCollectionChangeSet changeSet) {
        // `null`  means the async query returns the first time.
        if (changeSet == null) {
            notifyDataSetChanged();
            return;
        }
        // For deletions, the adapter has to be notified in reverse order.
        OrderedCollectionChangeSet.Range[] deletions = changeSet.getDeletionRanges();
        for (int i = deletions.length - 1; i >= 0; i--) {
            OrderedCollectionChangeSet.Range range = deletions[i];
            notifyItemRangeRemoved(range.startIndex, range.length);
        }

        OrderedCollectionChangeSet.Range[] insertions = changeSet.getInsertionRanges();
        for (OrderedCollectionChangeSet.Range range : insertions) {
            notifyItemRangeInserted(range.startIndex, range.length);
        }

        OrderedCollectionChangeSet.Range[] modifications = changeSet.getChangeRanges();
        for (OrderedCollectionChangeSet.Range range : modifications) {
            notifyItemRangeChanged(range.startIndex, range.length);
        }
    }
};

The RealmRecyclerViewAdapter provide this out of the box.

Object notifications

Realm supports object-level notifications. You may register a notification on a particular RealmObject in order to be notified if the object is deleted, or whenever any of the managed fields on the object have their values modified.

Only managed RealmObject can have listeners registered on them.

These changes can be accessed via the ObjectChangeSet parameter that is passed to the change listener. The ObjectChangeSet holds information about which fields were changed and if the RealmObject was deleted.

The ObjectChangeSet.isDeleted() will return true if the object was deleted. After that the listener won’t be called again.

The ObjectChangeSet.getChangedFields() will return the names of changed fields if any of the object’s managed fields were changed. You can also use ObjectChangeSet.isFieldChanged() to test if a given field was just changed.

private final RealmObjectChangeListener<Dog> listener = new RealmObjectChangeListener<Dog>() {
    @Override
    public void onChange(Dog dog, ObjectChangeSet changeSet) {
        if (changeSet.isDeleted()) {
            Log.i(TAG, "The dog was deleted");
            return;
        }

        for (String fieldName : changeSet.getChangedFields()) {
            Log.i(TAG, "Field " + fieldName + " was changed.");
        }
    }
};

Migrations

When working with any database, it is likely your model classes (i.e. your database schema) will change over time. Since model classes in Realm are defined as standard objects, changing the schema is as easy as changing the interface of the corresponding RealmObject subclass.

If you have no data stored on disk under the old database schema, just changing your code to the new definition will work fine. But if you do, there will be a mismatch between what Realm sees defined in code & the data Realm sees on disk, so an exception will be thrown. This can be avoided by setting the schema version and migration code in RealmConfiguration.

RealmConfiguration config = new RealmConfiguration.Builder()
    .schemaVersion(2) // Must be bumped when the schema changes
    .migration(new MyMigration()) // Migration to run instead of throwing an exception
    .build()

Using this, the migration code will be run automatically if needed. We provide built-in methods so you can upgrade your schema on disk, and the data you stored for previous versions of the schema.

// Example migration adding a new class
RealmMigration migration = new RealmMigration() {
  @Override
  public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {

     // DynamicRealm exposes an editable schema
     RealmSchema schema = realm.getSchema();

     // Migrate to version 1: Add a new class.
     // Example:
     // public Person extends RealmObject {
     //     private String name;
     //     private int age;
     //     // getters and setters left out for brevity
     // }
     if (oldVersion == 0) {
        schema.create("Person")
            .addField("name", String.class)
            .addField("age", int.class);
        oldVersion++;
     }

     // Migrate to version 2: Add a primary key + object references
     // Example:
     // public Person extends RealmObject {
     //     private String name;
     //     @PrimaryKey
     //     private int age;
     //     private Dog favoriteDog;
     //     private RealmList<Dog> dogs;
     //     // getters and setters left out for brevity
     // }
     if (oldVersion == 1) {
        schema.get("Person")
            .addField("id", long.class, FieldAttribute.PRIMARY_KEY)
            .addRealmObjectField("favoriteDog", schema.get("Dog"))
            .addRealmListField("dogs", schema.get("Dog"));
        oldVersion++;
     }
  }
}

See our migrationSample app for more details.

If there is no file on disk when Realm launches, no migration is needed, and Realm will just create a new .realm file & schema based on the latest models defined in your code. This means that if you are in the middle of development and changing your schema very often, and you are OK with losing all your data, you can delete your .realm file on disk (and the entire dataset it contained!) instead of having to write a migration. This can be helpful when tinkering with models early in the development cycle of your app.

RealmConfiguration config = new RealmConfiguration.Builder()
    .deleteRealmIfMigrationNeeded()
    .build()

Encryption

Please take note of the Export Compliance section of our LICENSE, as it places restrictions against the usage of Realm if you are located in countries with an export restriction or embargo from the United States.

The Realm file can be stored encrypted on disk by passing a 512-bit encryption key (64 bytes) to RealmConfiguration.Builder.encryptionKey():

byte[] key = new byte[64];
new SecureRandom().nextBytes(key);
RealmConfiguration config = new RealmConfiguration.Builder()
  .encryptionKey(key)
  .build();

Realm realm = Realm.getInstance(config);

This ensures that all data persisted to disk is transparently encrypted and decrypted with standard AES-256 encryption. The same encryption key must be supplied each time a Realm instance for the file is created.

See examples/encryptionExample for a complete example of how to securely store keys between runs in the Android KeyStore so that other applications cannot read them.

Adapters

Realm provides abstract utility classes to help bind data coming from OrderedRealmCollection(both RealmResults and RealmList implement this interface) to UI widgets.

To use those adapters, you need to add extra dependencies to the application level build.gradle.

dependencies {
	compile 'io.realm:android-adapters:2.0.0'
}

Intents

Since you cannot pass RealmObjects directly we advocate that you pass an identifier of the object you’re working with. A prime example would be if an object has a primary key. Pass that primary key value through an Intent via the extras bundle:

// Assuming we had a person class with a @PrimaryKey on the 'id' field ...
Intent intent = new Intent(getActivity(), ReceivingService.class);
intent.putExtra("person_id", person.getId());
getActivity().startService(intent);

Retrieve the primary key value from the bundle on the receiving component (Activity, Service, IntentService, BroadcastReceiver, etc) and then open a Realm and query for the RealmObject in the receiving component:

// in onCreate(), onHandleIntent(), etc.
String personId = intent.getStringExtra("person_id");
Realm realm = Realm.getDefaultInstance();
try {
    Person person = realm.where(Person.class).equalTo("id", personId).findFirst();
    // do something with the person ...
} finally {
    realm.close();
}

Full working examples can be found in the Object Passing portion of the threading example. The example shows you how to pass id’s and retrieve the RealmObject in very common Android use cases.

Strategies When Dealing With Android Framework Threads

Be careful when working with these classes:

The AsyncTask class contains the doInBackground() method which executes a background thread. The IntentService class contains the onHandleIntent(Intent intent) method which executes in a worker thread.

If you need to use Realm in either of these methods you should open the Realm, perform your work and then close the Realm before exiting. Below are a couple of examples.

AsyncTask

Open and close the Realm in the doInBackground() method, as shown below.

private class DownloadOrders extends AsyncTask<Void, Void, Long> {
    protected Long doInBackground(Void... voids) {
        // Now in a background thread.

        // Open the Realm
        Realm realm = Realm.getDefaultInstance();
        try {
            // Work with Realm
            realm.createAllFromJson(Order.class, api.getNewOrders());
            Order firstOrder = realm.where(Order.class).findFirst();
            long orderId = firstOrder.getId(); // Id of order
            return orderId;
        } finally {
            realm.close();
        }
    }

    protected void onPostExecute(Long orderId) {
        // Back on the Android mainThread
        // do something with orderId such as query Realm
        // for the order and perform some operation with it.
    }
}

IntentService

ChangeListeners will not work in an IntentService. Even though it is a Looper thread, each invocation of onHandleIntent() is a separate event that doesn’t “loop”. This means that it is possible to register change listeners, but they will never be triggered.

Open and close the Realm in the onHandleIntent() method, as shown below.

public class OrdersIntentService extends IntentService {
    public OrdersIntentService(String name) {
        super("OrdersIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        // Now in a background thread.

        // Open the Realm
        Realm realm = Realm.getDefaultInstance();
        try {
            // Work with Realm
            realm.createAllFromJson(Order.class, api.getNewOrders());
            Order firstOrder = realm.where(Order.class).findFirst();
            long orderId = firstOrder.getId(); // Id of order
        } finally {
            realm.close();
        }
    }
}

Other Libraries

This section describes how you can integrate Realm with other commonly used libraries for Android.

GSON

GSON is a library created by Google for deserializing and serializing JSON. GSON should work with Realm out of the box.

// Using the User class
public class User extends RealmObject {
    private String name;
    private String email;
    // getters and setters left out ...
}

Gson gson = new GsonBuilder().create();
String json = "{ name : 'John', email : 'john@corporation.com' }";
User user = gson.fromJson(json, User.class);

You can also see an example of how GSON can work with Realm in our GridViewExample.

Serialization

For full compatibility with libraries like Retrofit you will often want to be able to both deserialize and serialize an object. Serializing Realm objects to JSON does not work with GSON’s default behavior as GSON will use field values instead of getters and setters.

To make GSON serialization work with Realm you will need to write a custom JsonSerializer for each object that can be serialized and register it as a TypeAdapter.

This Gist shows how it can be done.

Primitive lists

Some JSON APIs will return arrays of primitive types like integers or Strings, which Realm doesn’t support yet. If it is not possible to change the JSON API, you can write a custom TypeAdapter for GSON that automatically maps between the primitive type from JSON and the wrapper object used by Realm.

In this Gist is an example of using a wrapper object for Integers, but the template can be used for all primitive arrays with datatypes supported by Realm.

Troubleshooting

Realm objects can contain fields that internally contain circular references. When this happens GSON can throw a StackOverflowError. We have seen this happen when a Realm object has a Drawable field:

public class Person extends RealmObject {
    @Ignore
    Drawable avatar;
    // other fields, etc
}

The Person class above contains an Android Drawable that has the @Ignore annotation applied. During GSON serialization the Drawable was being inspected and caused a StackOverflowError (GitHub Issue). To alleviate this, add the following code to your shouldSkipField method.

public boolean shouldSkipField(FieldAttributes f) {
  return f.getDeclaringClass().equals(RealmObject.class) || f.getDeclaringClass().equals(Drawable.class);
}

Please note the Drawable.class evaluation. This tells GSON to skip this field during serialization. Adding this will alleviate the StackOverflowError.

Jackson-databind

Jackson-databind is a library for binding JSON data to Java classes.

Jackson uses reflection to perform the databinding. This conflicts with Realm’s support for RxJava as RxJava might not be available to the class loader. This can result in an exception that looks like this:

java.lang.NoClassDefFoundError: rx.Observable
at libcore.reflect.InternalNames.getClass(InternalNames.java:55)
...

This can be fixed by either adding RxJava to your project or create an empty dummy file that looks like the following.

package rx;

public class Observable {
    // Dummy class required for Jackson-Databind support if
    // RxJava is not a project dependency.
}

This issue has also been reported to the Jackson project here.

Kotlin

Realm is fully compatible with Kotlin programming language, but there are a few caveats to be aware of:

See this example for a working app combining Realm and Kotlin.

Parceler

Parceler is a library that automatically generates the boilerplate required to make an object respect the Parcelable interface. Due to Realm’s use of proxy classes, Parceler requires the following setup to work with Realm’s model classes.

// All classes that extend RealmObject will have a matching RealmProxy class created
// by the annotation processor. Parceler must be made aware of this class. Note that
// the class is not available until the project has been compiled at least once.
@Parcel(implementations = { PersonRealmProxy.class },
        value = Parcel.Serialization.BEAN,
        analyze = { Person.class })
public class Person extends RealmObject {
    // ...
}

If you are using Gradle for getting Parceler, please make sure the following lines are there (see here for more details):

compile "org.parceler:parceler-api:1.0.3"
apt "org.parceler:parceler:1.0.3"

There are some important restrictions to be aware of when using Parceler:

  1. If your model contains a RealmList you need to register a special adapter.
  2. Once an object has been parcelled, it becomes detached from Realm and at this point behaves like an unmanaged object containing a snapshot of the data. Further changes to this object will not be persisted in Realm.

Retrofit

Retrofit is a library from Square that makes it easy to work with a REST API in a typesafe manner.

Realm will work with both Retrofit 1.* and 2.* out of the box, but note that Retrofit does not automatically add objects to Realm, instead you must manually add them using the realm.copyToRealm() or realm.copyToRealmOrUpdate() methods.

GitHubService service = restAdapter.create(GitHubService.class);
List<Repo> repos = service.listRepos("octocat");

// Copy elements from Retrofit to Realm to persist them.
realm.beginTransaction();
List<Repo> realmRepos = realm.copyToRealmOrUpdate(repos);
realm.commitTransaction();

Robolectric

Robolectric is a library that allows you to run JUnit tests directly in the JVM instead of in a phone or emulator. Currently Robolectrics does not support native libraries like those that are bundled with Realm. This means that for now it is not possible to test Realm using Robolectric.

You can follow the feature request here: https://github.com/robolectric/robolectric/issues/1389

RxJava

RxJava is a Reactive Extensions library from Netflix that extends the Observer pattern. It makes it possible to observe changes to data as composable sequences.

Realm has first-class support for RxJava, which means that the following Realm classes can be exposed as an Observable: Realm, RealmResults, RealmObject, DynamicRealm and DynamicRealmObject.

// Combining Realm, Retrofit and RxJava (Using Retrolambda syntax for brevity)
// Load all persons and merge them with their latest stats from GitHub (if they have any)
Realm realm = Realm.getDefaultInstance();
GitHubService api = retrofit.create(GitHubService.class);
realm.where(Person.class).isNotNull("username").findAllAsync().asObservable()
    .filter(persons.isLoaded)
    .flatMap(persons -> Observable.from(persons))
    .flatMap(person -> api.user(person.getGithubUserName())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(user -> showUser(user));

Please note, Asynchronous Queries are non blocking and the code above will immediately return a RealmResults instance. If you want to ensure you’re only operating on a loaded list you should filter the Observable via the filter operator and check the list by calling the RealmResults<E>.isLoaded() method. By checking to see if the RealmResults are loaded you can then determine if your query has completed or not.

See the RxJava sample project for more examples.

Configuration

RxJava is an optional dependency, which means that Realm doesn’t automatically include it. This has the benefit that you can choose which version of RxJava to use as well as avoid the method count bloat in projects that does not use RxJava. RxJava has to be manually added to the build.gradle file.

dependencies {
  compile 'io.reactivex:rxjava:1.1.0'
}

It is possible to configure how Realm creates Observables by creating a custom RxObservableFactory. This is configured using RealmConfiguration.

RealmConfiguration config = new RealmConfiguration.Builder()
  .rxFactory(new MyRxFactory())
  .build()

If no RxObservableFactory is defined, Realm defaults to RealmObservableFactory which is a class provided by Realm that supports RxJava <= 1.1.*.

Testing and Debugging

See our unitTestExample for information on how Realm can be combined with JUnit3, JUnit4, Robolectric, Mockito and PowerMock.

Android Studio Debugging

There’s a small gotcha you have to be aware of when working using Android Studio or IntelliJ. The debugger can provide misleading values depending on the debugging view you’re using.

For example, adding a watch in Android Studio on a RealmObject will display values of the fields. Unfortunately these values are wrong because the field values are not used. Realm creates a proxy object behind the scenes and overrides the getters and setters in order to access the persisted data in the Realm (more info). Adding a watch for any of the accessors will yield correct values. See the image below: Android Studio, IntelliJ Debug RealmObject

In the image above the debugger has stopped on line 113. There are three watch values, the person variable and the person.getName() and person.getAge() accessors. The code from lines 107 to 111 alters the person instance by changing the name and age. These values are then persisted in a transaction. On line 113, where the debugger is currently paused, the person watch instance is reporting on field values and they are incorrect. The watch values that use the accessor for person.getName() and person.getAge() report values that are correct.

Please note, the .toString() method will output the correct values but the watch panel will not (when watching a variable which is a RealmObject).

NDK Debugging

Realm is a library that contains native code. We recommend that you use a crash reporting tool, such as Crashlytics, to track native errors so we are in a better position to help you if something goes wrong.

Debugging NDK crashes is usually cumbersome as the default stack trace provides minimal information that can be of use. Crashlytics will allow you to capture valuable NDK crash information. To enable NDK crash reporting in Crashlytics, please follow the steps outlined in this guide.

To enable NDK crash reporting for your project, add this to the root of your build.gradle file. Please note, the values androidNdkOut and androidNdkLibsOut are not needed.

crashlytics {
  enableNdk true
}

Current Limitations

Realm is generally trying to have as few constraints as possible and we are continuously adding new features based on feedback from the community. However, Realm still has a few limitations. These have been compiled below.

Please refer to our GitHub issues for a more comprehensive list of known issues.

General

Realm aims to strike a balance between flexibility and performance. In order to accomplish this goal, realistic limits are imposed on various aspects of storing information in a Realm. For example:

  1. The upper limit of class names is 57 characters. Realm for Android prepend class_ to all names, and the browser will show it as part of the name.
  2. The length of field names has a upper limit of 63 character.
  3. It is not possible to have two model classes with the same name in different packages.
  4. Nested transactions are not supported, and an exception is thrown if they are detected.
  5. Strings and byte arrays (byte[]) cannot be larger than 16 MB.

Sorting and querying on String

Sorting and case insensitive string matches in queries are only supported for character sets in ‘Latin Basic’, ‘Latin Supplement’, ‘Latin Extended A’, ‘Latin Extended B’ (UTF-8 range 0-591). Moreover, sorting is done so capital and non-capital letters are sorted togther unlike what standard libraries do. That means that '- !"#0&()*,./:;?_+<=>123aAbBcCxXyYzZ is the actual sorting order in Realm. In addition, setting the case insensitive flag in queries when using equalTo(), notEqualTo(), contains(), endsWith(), beginsWith() or like() will only work on characters from the English locale. Read more about these limitations here.

Threads

Although Realm files can be accessed by multiple threads concurrently, you cannot hand over Realms, Realm objects, queries, and results between threads. The thread example shows how to use Realm in a multithreading environment. Read more about Realm’s threading.

Realm files cannot be accessed by concurrent processes

Although Realm files can be accessed by multiple threads concurrently, they can only be accessed by a single process at a time. Different processes should either copy Realm files or create their own. Multi-process support is coming soon.

RealmObject’s hashCode

A RealmObject is a live object, and it might be updated by changes from other threads. Although two Realm objects returning true for RealmObject.equals() must have the same value for RealmObject.hashCode(), the value is not stable, and should be neither used as a key in HashMap nor saved in HashSet.

Best Practices

Working With Android

Out of the box, Realm works seamlessly with Android. The main thing that you have to keep in mind is that RealmObjects are thread confined. The importance of understanding this comes into play more when you want to start passing Realm objects between activities, to background services, broadcast receivers and more.

Preventing Application Not Responding Errors (ANR’s)

Typically Realm is fast enough to read and write data on Android’s main thread. However, write transactions are blocking across threads so in order to prevent accidental ANR’s we advise that you perform all Realm write operations on a background thread (not Android’s main thread). Learn how to perform operations on the background thread via Realms Asynchronous Transactions.

Controlling the lifecycle of Realm instances

Choosing the proper lifecycle for a Realm instance is a balancing act. Because RealmObjects and RealmResults are accessed through a lazy cache, keeping a Realm instance open for as long as possible not only avoids the overhead incurred in opening and closing it but is likely to allow queries against it to run more quickly. On the other hand, an open Realm instance holds significant resources, some of which are not controlled by the Java memory manager. Java cannot automatically administer these resources. It is essential that code that opens a Realm instance closes it when it is no longer needed.

Realm uses an internal reference counted cache so that, after getting the first Realm instance, getting subsequent instances on the same thread is free. The underlying resources are released, though, only when all of the instances on that thread are closed.

One reasonable choice is to make the lifecycle of the Realm instance congruent with the lifecycles of views that observe it. The examples below demonstrate this using a Fragment and an Activity, each with a RecyclerView that displays data retrieved from a Realm instance. In both examples the Realm instance and the RecyclerView Adapter are initialized in the create method and closed in the corresponding destroy method. Note that this is safe, even for the Activity: the database will be left in a consistent state even if the onDestroy and close methods are never called.

Clearly, if most of the Fragments associated with an Activity require access to the same dataset, it would make sense for the Activity, not the individual Fragments, to control the lifecycle of the instance.

// Setup Realm in your Application
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Realm.init(this);
        RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().build();
        Realm.setDefaultConfiguration(realmConfiguration);
    }
}

// onCreate()/onDestroy() overlap when switching between activities.
// Activity2.onCreate() will be called before Activity1.onDestroy()
// so the call to getDefaultInstance in Activity2 will be fast.
public class MyActivity extends Activity {
    private Realm realm;
    private RecyclerView recyclerView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        realm = Realm.getDefaultInstance();

        setContentView(R.layout.activity_main);

        recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        recyclerView.setAdapter(
            new MyRecyclerViewAdapter(this, realm.where(MyModel.class).findAllAsync()));

        // ...
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        realm.close();
    }
}

// Use onCreateView()/onDestroyView() for Fragments.
// Note that if the db is large, getting the Realm instance may, briefly, block rendering.
// In that case it may be preferable to manage the Realm instance and RecyclerView from
// onStart/onStop instead. Returning a view, immediately, from onCreateView allows the
// fragment frame to be rendered while the instance is initialized and the view loaded.
public class MyFragment extends Fragment {
    private Realm realm;
    private RecyclerView recyclerView;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        realm = Realm.getDefaultInstance();

        View root = inflater.inflate(R.layout.fragment_view, container, false);

        recyclerView = (RecyclerView) root.findViewById(R.id.recycler_view);
        recyclerView.setAdapter(
            new MyRecyclerViewAdapter(getActivity(), realm.where(MyModel.class).findAllAsync()));

        // ...

        return root;
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        realm.close();
    }
}

Reuse RealmResults and RealmObjects

On the UI thread and all other Looper threads, all RealmObjects and RealmResults are automatically refreshed when changes are made to the Realm. This means that it isn’t necessary to fetch those objects again when reacting to a RealmChangedListener. The objects are already updated and ready to be redrawn on the screen.

public class MyActivity extends Activity {

    private Realm realm;
    private RealmResults<Person> allPersons;
    private RealmChangeListener realmListener = new RealmChangeListener() {
        @Override
        public void onChange(Realm realm) {
            // Just redraw the views. `allPersons` already contain the
            // latest data.
            invalidateView();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        realm = Realm.getDefaultInstance();
        realm.addRealmChangeListener(listener);
        allPerson = realm.where(Person.class).findAll(); // Create the "live" query result
        setupViews(); // Initial setup of views
        invalidateView(); // Redraw views with data
    }

    // ...
}

Auto-incrementing IDs

Auto-incrementing IDs are not supported by Realm by design. This is primarily because it is impossible to generate such keys in a distributed environment, and compatibility between data stored in a local Realm and a synchronized Realm is a high priority. Note that Realm does not need primary keys in order to create relationships.

It is still possible to efficiently create primary keys with Realm that satisfy the use cases provided by auto-incrementing IDs, but it is important to identify what the auto-incrementing ID is used for:

1) To provide a unique identifier in order to identify the object. This can be replaced by a GUID, which guarentees uniqueness for all practical purposes and can be created by a device even when it is offline:

public class Person extends RealmObject {
    @PrimaryKey
    private String id = UUID.randomUUID().toString();
    private String name;
}

2) To provide loose insertion order. An example is sorting tweets. This can be replaced by a createdAt field, which doesn’t need to be a primary key:

public class Person extends RealmObject {
    @PrimaryKey
    private String id = UUID.randomUUID().toString();
    private Date createdAt = new Date();
    private String name;
}

3) To provide strict insertion order. An example is a list of tasks. This can be modelled using a RealmList which will guarantee insertion order, even if devices have been offline.

public class SortedPeople extends RealmObject {
    @PrimaryKey
    private int id = 0
    private RealmList<Person> persons;
}

public class Person extends RealmObject {
    private String name;
}

// Create wrapper object when creating object
RealmConfiguration config = new RealmConfiguration.Builder()
  .initialData(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        realm.insert(new SortedPeople());
    }
  });

// Insert objects through the wrapper
realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        SortedPeople sortedPeople = realm.where(SortedPeople.class).findFirst();
        sortedPeople.getPersons().add(new Person());
    }
});

If you have a use case where you still think auto-incrementing IDs will be a better fit, you can use this helper class, but note that keys generated using this class are not usable if you:

1) Create Realm Objects in multiple processes. 2) Want to share the Realm between multiple devices at some point in the future.

For auto-incrementing IDs that are safe to create across processes, you will need to query for the max value each time you begin a transaction:

realm.beginTransaction();
Number maxValue = realm.where(MyObject.class).max("primaryKeyField");
long pk = (maxValue != null) ? maxValue + 1 : 0;
realm.createObject(MyObject.class, pk++);
realm.createObject(MyObject.class, pk++);
realm.commitTransaction();

Recipes

We’ve put together some recipes showing how to use Realm to accomplish a few specific tasks. We add more recipes regularly, so check back often. If there’s an example you’d like to see, please open an issue on GitHub.

Sync

The Realm Mobile Platform (RMP) extends the Realm Mobile Database across the network, enabling automatic synchronization of data across devices. In order to do this, a new set of types and classes are provided that support these synchronized Realms. This sync-specific API is strictly additive to the existing Realm Mobile Database.

Enabling Realm Mobile Platform

In order to enable Realm Mobile Platform, add the following section to your app’s build.gradle:

realm {
  syncEnabled = true;
}

Yes, it is as simple as that!

Users

The central object in the Realm Object Server is the Realm User (SyncUser) associated with a synchronized Realm. A SyncUser can be authenticated to a shared Realm via a username/password scheme, or through a number of third-party authentication methods.

Creating and logging in a user requires two things:

  • A URL of a Realm Object Server to connect to.
  • Credentials for an authentication mechanism that describes the user as appropriate for that mechanism (i.e., username/password, access key, etc).

Server URL

The authentication server URL is simply a URL representing the location of the Realm Object Server.

String serverUrl = "http://my.realmserver.com:9080";

For more details about the server URL, please refer to our authentication documentation.

Authentication

Authentication is used to establish the identity of users and log them in. Refer to our authentication documentation for a list of authentication providers supported by the Realm Mobile Platform.

The credential information for a given user can be created in one of several ways:

  • Providing a valid username/password combination
  • Providing a token obtained from a supported third-party authentication service
  • Providing a token and a custom authentication provider (see Custom Authentication)

The username and password authentication is entirely managed by the Realm Object Server, giving you full control over your application’s user management. For other authentication methods, your application is responsible for logging into the external service and obtaining the authentication token.

Here are some examples of setting credentials with various providers.

Username/Password
SyncCredentials myCredentials = SyncCredentials.usernamePassword(username, password, true);

The third parameter of usernamePassword() should only be true the first time, as it indicates that the user should be created. Once the user is created, the parameter must be set to false.

Google
String token = "..."; // a string representation of a token obtained by Google Login API
SyncCredentials myCredentials = SyncCredentials.google(token);
Facebook
String token = "..."; // a string representation of a token obtained by Facebook Login API
SyncCredentials myCredentials = SyncCredentials.facebook(token);
Custom Authentication
String token = "..."; // a string representation of a token obtained from your authentication server
Map<String, Object> customData = new HashMap<>();
SyncCredentials myCredentials = SyncCredentials.custom(
  token,
  'myauth',
  customData,
);

Note: it is possible to pass additional login information to this constructor as a third parameter. Please see the API reference for more information.

SSL/TLS

In order to enable TLS (using realms://) you need to provide the trusted root certificate of the server in PEM format.

Example:

SyncConfiguration syncConfiguration = new SyncConfiguration.Builder(user, "realms://host/~/myapp")
                .serverCertificate("root_ca.pem")
                .build();
realm = Realm.getInstance(syncConfiguration);
  • The certificate root_ca.pem needs to be under the assets directory of your app.
.
├── main
│   ├── AndroidManifest.xml
│   ├── assets
│   │   └── root_ca.pem
│   ├── java
  • It is your responsibility to download and verify the correct PEM certificate for the root CA you trust. The list by Mozilla can be used.

Alternatively, should you wish to disable certificate verification (say for debug or when using a self-signed certificate) you can do so by calling disableSSLVerification.

SyncConfiguration syncConfiguration = new SyncConfiguration.Builder(user, "realms://host/~/myapp")
                .disableSSLVerification()
                .build();
realm = Realm.getInstance(syncConfiguration);

Logging in

Now, with all the required parameters the user can now be logged into the Realm Object Server:

String authURL = "http://my.realm-auth-server.com:9080/auth";
SyncUser user = SyncUser.login(myCredentials, authURL);

The Realm Mobile Platform allows an application to support multiple users at the same time.

For example, consider an email client app that supports connecting to multiple email accounts. Multiple users (one for each email account) can be authenticated at any given time, with all active users being available via SyncUser.all().

Logging Out

Logging out of a synced Realm is simple:

user.logout();

When a user is logged out, the synchronization will stop. It is only possible to log out once all Realms belonging to the user have have been closed. A logged out user can no longer open a Realm using a SyncConfiguration.

Working with Users

It is possible to retrieve the current user. The current user is the last user who logged in and credentials are not expired yet.

SyncUser user = SyncUser.currentUser();

You can obtain a JSON representation of the user and its credentials:

String userJson = user.toJson();

By storing this user presentation, the user does not have to log in again to any 3rd party login provider. Instead, you can recreate the user object and refresh access tokens at the Realm Object Server by

SyncUser user = SyncUser.fromJson(userJson);

Realm saves the current user using a UserStore. The default UserStore is backed by a private Shared Preference file, but this behaviour can be overridden using SyncManager.setUserStore(userStore).

It is important to remember that users - and their credentials - are considered sensitive data.

If there are multiple users logged in, you can retreive all users:

Map<String, SyncUser> users = SyncUser.all();

If no users are logged in, the map returned will be empty.

Admin users

Admin users are Realm Object Server users that have management-level access to all Realms on a ROS instance. Whether or not a user is an admin user is returned by the SyncUser.isAdmin() method, which reflects the user’s state at the time of its last successful login.

SyncUser user = SyncUser.login(credentials, serverURL);
if (user != null) {
    // user.isAdmin() => the user is an admin user
}

Opening a Synchronized Realm

For standalone Realms, RealmConfiguration is used to configure the options for a Realm. Realm Mobile Platform for Android is using an extended configuration class called SyncConfiguration. The configuration ties together an authenticated user and a sync server URL.

The sync server URL may contain the tilde character (“~”) which will be transparently expanded to represent the user’s unique identifier. This scheme easily allows you to write your app to cater to its individual users. The location on disk for shared Realms is managed by the framework, but can be overridden if desired.

SyncUser user = getUserFromLogin();
String serverURL = "realm://my.realm-server.com:9080/~/default";
SyncConfiguration configuration = new SyncConfiguration.Builder(user, serverURL).build();

Notice: URLs cannot end with a .realm file extension. They should contain the main part of the name (e.g., default above). Realm will create all the related files and folders to store the data.

Once a SyncConfiguration is constructed, synchronized Realm instances are created as for normal Realms:

Realm realm = Realm.getInstance(syncConfiguration);

In some cases, you might not want to open a Realm until it has all remote data available. For example, if you want to show the users a list of all available zip codes. In that case you can use the getInstanceAsync API with the waitForRemoteInitialData() flag. This will download the Realm in the background and not report it ready until all remote data are downloaded:

SyncUser user = getUser();
String url = getUrl();
SyncConfiguration config = new SyncConfiguration.Builder(user, url)
    .waitForRemoteInitialData();
    .build();

RealmAsyncTask task = Realm.getInstanceAsync(config, new Realm.Callback() {
    @Override
    public void onSuccess(Realm realm) {
        // Realm is now downloaded and ready. New changes to Realm will continue
        // to be synchronized in the background as normal.
    }
});

Sync Sessions

A synced Realm’s connection to the Realm Object Server is represented by a SyncSession object. A session object for a specific Realm can be retrieved by using the SyncManager.getSession(SyncConfiguration) API.

Progress Notifications

Session objects allow your app to monitor the status of a session’s uploads to and downloads from the Realm Object Server by registering progress notifications on a session object.

Progress notifications will be invoked periodically by the synchronization subsystem. The notification callback will happen on a worker thread, so manipulating UI elements must be done using Activity.runOnUiThread(Runnable) or similar. As many notifications as needed can be registered on a session object simultaneously. Notifications can either be configured to report upload progress or download progress.

Each time a notification is called, it will receive the current number of bytes already transferred, as well as the total number of transferrable bytes (defined as the number of bytes already transferred plus the number of bytes pending transfer).

Notifications can be un-registered again by using SyncSession.removeProgressListener(listener).

There are two types of notification modes: ProgressMode.CURRENT_CHANGES and ProgressMode.INDEFINITELY.

Notifications configured with ProgressMode.INDEFINITELY will remain active unless explicitly stopped by the user and will always report the most up-to-date number of transferrable bytes. This type of block could be used to control a network indicator UI that, for example, changes color or appears only when uploads or downloads are actively taking place. A notification in this mode can report Progress.isTransferComplete() multiple times.

ProgressListener listener = new ProgressListener() {
    @Override
    public void onChange(Progress progress) {
        activity.runOnUiThread(new Runnable) {
            @Override
            public void run() {
                if (progress.isTransferComplete()) {
                    hideActivityIndicator();
                } else {
                    showActivityIndicator();
                }
            }
        }
    }
};

SyncSession session = SyncManager.getSession(getSyncConfiguration());
session.addDownloadProgressListener(ProgressMode.INDEFINITELY, listener);

// When stopping activity
session.removeProgressListener(listener);

Notifications configured with ``ProgressMode.CURRENT_CHANGES only report progress for _currently outstanding work_. These notifications capture the number of transferrable bytes at the moment they are registered and always report progress relative to that value. Once the number of transferred bytes reaches or exceeds that initial value, the notification will send one final event where Progress.isTransferComplete() is true` and then never again. The listener should be unregistered at this point. This type of notification could, for example, be used to control a progress bar that tracks the progress of an initial download of a synced Realm when a user signs in, letting them know when their local copy is up-to-date.

final SyncSession session = SyncManager.getSession(getSyncConfiguration());
ProgressListener listener = new ProgressListener() {
    @Override
    public void onChange(Progress progress) {
        activity.runOnUiThread(new Runnable) {
            @Override
            public void run() {
                if (progress.isTransferComplete()) {
                    hideProgressBar();
                    session.removeProgressListener(this);
                } else {
                    updateProgressBar(progress.getFractionTransferred());
                }
            }
        }
    }
};
setupProgressBar();
session.addDownloadProgressListener(ProgressMode.CHANGES_ONLY, listener);

Access Control

The Realm Mobile Platform provides flexible access control mechanisms to restrict which users are allowed to sync against which Realm files. This can be used, for example, to create collaborative apps where multiple users write to the same Realm. It can also be used to share data in a publisher/subscriber scenario where a single writing user shares data with many users with read permissions.

There are three axes that control the access level (permissions) of a given Realm:

  • mayRead indicates that the user is allowed to read from the Realm,
  • mayWrite indicates that the user is allowed to write to the Realm,
  • mayManage indicates that the user is allowed to change the permissions for the Realm.

Unless permissions are explicitly modified, only the owner of a Realm can access it. The only exception is admin users: They are always granted all permissions to all Realms on the server.

Write-only permissions (i.e., mayWrite set without mayRead) are not currently supported.

Please refer to the general Realm Object Server documentation on Access Control to learn more about the concept.

Management Realm

All access level management operations can be performed by writing to the Management Realm. The Management Realm is just like a regular synchronized Realm, but that the Realm Object Server is specifically designed to react to by default. Permission Change objects can be added to the Realm to apply those changes to the access control settings of a Realm file.

To obtain the management Realm for a given Sync User, call its SyncUser.getManagementRealm() method. This Realm follows the normal rules for Realms and should be closed when no longer used.

Modifying Permissions

Modifying the access control settings for a Realm file is performed through one of two means: change objects and offer/response objects.

PermissionChange

The PermissionChange object allows you to directly control a Realm’s access settings by writing the change objects to the Management Realm.

SyncUser user = SyncUser.currentUser();
String realmURL = "realm://ros.example.com/~/default"; // The remote Realm URL on which to apply the changes
String anotherUserID = "other-user-id"; // The user ID for which these permission changes should be applied

Realm realm = user.getManagementRealm();
realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        Boolean mayRead = true; // Grant read access
        Boolean mayWrite = null; // Keep current permission
        boolean mayManage = false; // Revoke management access
        PermissionChange change = new PermissionChange(realmUrl,
                                                       anotherUserID,
                                                       mayRead,
                                                       mayWrite,
                                                       mayManage);
        realm.insert(change);
    }
});

To apply the permission changes for all Realms managed by the Sync User, specify a realmURL value of *. To apply the permission changes for all Sync Users authorized with the Object Server, specify a userID value of *.

Once the Object Server has processed the operations encoded in the PermissionChange object, it will set that object’s status and statusMessage fields.

To be notified of the result of the permission change operation, simply observe the permission change object like any other Realm object. See Notifications for details.

Here’s one way to be notified of the status of permission change operations:

SyncUser user = SyncUser.currentUser();
Realm realm = user.getManagementRealm();
final String permissionId = "permission-id";
PermissionChange change = realm.where(PermissionChange.class).equalTo("id", permissionId).findFirst();
change.addChangeListener(new RealmChangeListener<PermissionChange>() {
    @Override
    public void onChange(PermissionChange change) {
        if (change.getId().equals(permissionId)) {
            Integer status = change.getStatusCode();
            if (status == 0) {
                // Handle success
                realm.close();
            } else if (status > 0) {
                // Handle error
            }
        }
    }
});
PermissionOffer/Response

The PermissionOffer and PermissionOfferResponse classes allow you to share Realms between users, all from the client APIs–no server code is necessary. To share a Realm, the following steps happen:

  1. Create a PermissionOffer object in the sharing user’s management Realm.
  2. Wait for the offer to be synced and processed by the Realm Object Server, which will populate the token property of the offer object.
  3. Send the token to another user via any method.
  4. The receiver creates a PermissionOfferResponse in their management Realm.
  5. Wait for the offer response to be synced and processed by the server, which will populate the realmUrl property of the response object.
  6. The receiving user can now access the shared Realm at the URL.
SyncUser user = getUser("user");
Realm managementRealm = user.getManagementRealm();
String sharedRealmUrl = "realm://my.server/~/my-realm";
boolean mayRead = true;
boolean mayWrite = true;
boolean mayManage = true;
Date expiresAt = null; // Offer never expires;
final PermissionOffer offer = new PermissionOffer(sharedRealmUrl, mayRead, mayWrite, mayManage, expiresAt);
String offerId = offer.getId();
managementRealm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        realm.insert(offer);
    }
});

// Wait for server to handle the offer
RealmResults<PermissionOffer> offers = managementRealm.where(PermissionOffer.class)
        .equalTo("id", offerId)
        .equalTo("statusCode", 0)
        .findAll();
offers.addChangeListener(new RealmChangeListener<RealmResults<PermissionOffer>>() {
    @Override
    public void onChange(RealmResults<PermissionOffer> offers) {
        PermissionOffer offer = offers.first();
        String token = offer.getToken();
        // Offer is ready, send token to the other user
        sendTokenToOtherUser(token);
    }
});

Similarly to PermissionChange, arguments control the read, write, and manage access for the Realm at the supplied realmURL. The expiresAt argument controls when the token will no longer be consumable. If you don’t pass a value to expiresAt, or pass nil, the offer will never expire. Note that users who have processed the offer token will not lose access after it expires.

Once another user has obtained the token, they can consume it and create a PermissionOfferResponse object:

final SyncUser user2 = getUser("user2");
Realm managementRealm = user.getManagementRealm();

// Create response with received token
String offerToken = getToken();
final PermissionOfferResponse offerResponse = new PermissionOfferResponse(offerToken);

// Add to management Realm to sync with ROS
managementRealm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        realm.insert(offerResponse);
    }
});

// After processing, offerResponse's realmUrl property will contain the URL of the shared Realm

Permissions granted by SyncPermissionOffer are additive: if a user already has write access and accepts an offer granting read permissions, they will not lose their write access.

A permission offer can be revoked by deleting the PermissionOffer object from the management Realm, or by setting its expiresAt property to a date in the past. This will prevent new users from accepting the offer, but will not revoke permissions of users who had consumed it prior to that.

Logging

Debugging synced Realms can be cumbersome, and logging is important to gain insights to issues. By enabling more verbose logs, you can better see what is happening through Android logcat.

RealmLog.setLevel(Log.VERBOSE);

Error reporting

It is possible to set up error handling by registering an error handler.

SyncConfiguration configuration = new SyncConfigurtion.Builder(user, serverURL)
  .errorHandler(new Session.ErrorHandler() {
    void onError(Session session, ObjectServerError error) {
        // do some error handling
    }
  })
  .build();

It is also possible to register a default global error handler that will apply to all SyncConfigurations:

SyncManager.setDefaultSessionErrorHandler(myErrorHandler);

Syncing Migrations

Migrations are applied automatically to synced Realms, with some limitations and caveats:

  • Additive changes, such as adding a class or adding a field to an existing class, are applied automatically.
  • Removing a field from a schema will not delete the field from the database, but rather instruct Realm to ignore that field. New objects will continue to be created with those properties, but they will be set to null. Non-nullable fields will be set to appropriate zero/empty values: 0 for numeric fields, an empty string for string properties, and so on.
  • Custom migration blocks cannot be set on synced Realm configration.
  • Destructive changes–that is, changes to a Realm schema that require changes to be made to code that interacts with that Realm–are not directly supported. This includes changes to field types that keep the same name e.g., changing from a nullable string to a non-nullable string or changing a primary key.
  • Setting a SyncConfiguration.schemaVersion() is entirely optional.

Custom migrations can be applied to synced Realms by using a DynamicRealm on the client side, or as a JavaScript function on the server using the Node.js SDK (if you are using an edition of Object Serve that supports it). However, if the migration makes a destructive change, the Realm will stop syncing with ROS, producing a Bad changeset received error.

If you have destructive changes to apply in a synced Realm migration, instead create a new synced Realm with the new schema, and create a migration function which listens for changes on the old Realm and copies values to the new Realm. As with the non-destructive migration, this can be either code on the client side in the new version of the app (with the new schema version), or Node.js code on the server side.

Conflict Resolution

Conflict resolution is documented in the Realm Object Server documentation

Limitations

Currently, Realm models have no support for final and volatile fields. This is mainly to avoid discrepancies between how an object would behave as managed by Realm or unmanaged.

Realm model classes are not allowed to extend any other object than RealmObject. If declared, the default constructor (constructor with no parameters) must always be empty. The reason is that a default contructor will call methods which assume a Realm instance is present. But that instance isn’t create before the contructor returns. You can add other constructors for your convienence.

FAQ

How can find and view the content of my Realm file(s)?

This SO question describes where to find your Realm file. You can then view the content with our Realm Studio.

How big is the Realm Base library?

Once your app is built for release and split for distribution, Realm should only add about 800KB to your APK in most cases. The releases we distribute are significantly larger because they include support for more architectures (ARM7, ARMv7, ARM64, x86, MIPS). The APK file contains all supported architectures but the Android installer will only install native code for the device’s architecture. As a consequence, the installed app is smaller than the size of the APK file.

It is possible to reduce the size of the Android APK itself by splitting the APK into a version for each architecture. Use the Android Build Tool ABI Split support by adding the following to your build.gradle:

android {
    splits {
        abi {
            enable true
            reset()
            include 'armeabi-v7a', 'arm64-v8a', 'mips', 'x86', 'x86_64'
        }
    }
}

Select the architectures that you’d like to include and a separate APK will be built for each. See the Android Tools documentation about ABI Splits for more information.

An example is also included on GitHub.

If you don’t want to handle multiple APK’s it is also possible to restrict the number of architectures supported in a single APK. This is done by adding abiFilters to your build.gradle:

android {
    defaultConfig {
        ndk {
            abiFilters 'armeabi-v7a', 'arm64-v8a', 'mips', 'x86', 'x86_64'
        }
    }
}

For more details about ABI splits and filters, read this article by Brijesh Masrani.

Is Realm open source?

Yes! Realm’s internal C++ storage engine and the language SDKs over it are entirely open source and licensed under Apache 2.0. Realm also optionally includes a closed-source Realm Platform Extensions component, but that is not required to use Realm as an embedded database.

What is the difference between a normal Java object and a Realm object?

The main difference is that a normal Java object contains its own data while a Realm object doesn’t contain data but get or set the properties directly in the database.

Instances of Realm objects can be either managed or unmanaged.

  • Managed objects are persisted in Realm, are always up to date and thread confined. They are generally more lightweight than the unmanaged version as they take up less space on the Java heap.

  • Unmanaged objects are just like ordinary Java objects, they are not persisted and they will not be updated automatically. They can be moved freely across threads.

It is possible to convert between the two states using Realm.copyToRealm() and Realm.copyFromRealm().

Why do model classes need to extend RealmObject?

We need to add Realm specific functionality to your model classes. It also allows us to use generics in our APIs, making it easier to read and use. If you don’t want to extend a base class you can instead implement the RealmModel interface.

What are the RealmProxy classes about?

The RealmProxy classes are our way of making sure that the Realm object doesn’t contain any data itself, but instead access the data directly in the database.

For every model class in your project, the Realm annotation processor will generate a corresponding RealmProxy class. This class extends your model class and is what is returned when you call Realm.createObject(), but from the point of view of your code you won’t notice any difference.

Why do I need to use transactions when writing Realm objects?

Transactions are needed to ensure multiple fields are updated as one atomic operation. It allows you to define the scope of the updates that must be either fully completed or not completed at all (in case of errors or controlled rollback). By specifying the scope of the transaction you can control how frequent (or fast) your updates are persisted (i.e. insert multiple objects in one operation).

When doing inserts in a normal SQL based database like SQLite you are inserting multiple fields at once. This is automatically wrapped in a transaction, but is normally not visible to the user. In Realm these transactions are always explicit.

What to do about out-of-memory exceptions?

Realm for Android is built upon an embedded storage engine. The storage engine does not allocate memory on the JVM heap but in native memory. When the storage engine cannot allocate native memory or the file system is full, Realm will throw an io.realm.internal.OutOfMemoryError exception. It is important to not ignore this error. If your app continues running, accessing the Realm file might leave your Realm file in a corrupted or an inconsistent state. In the case of io.realm.internal.OutOfMemoryError, it is safest to terminate the app.

Large Realm file size

You should expect a Realm database to take less space on disk than an equivalent SQLite database.

In order to give you a consistent view of your data, Realm operates on multiple versions of a Realm. If you read some data from a Realm and then block the thread on a long-running operation while writing to the Realm on other threads, the version is never updated and Realm has to hold on to intermediate versions of the data which you may not actually need, resulting in the file size growing steadily. (This extra space would eventually be reused by future writes, or could be compacted — for example by calling compactRealm.)

I see a network call to Mixpanel when I run my app, what is that?

Realm collects anonymous analytics when you run the Realm bytecode transformer on your source code. This is completely anonymous and helps us improve the product by flagging which version of Realm you use and what OS you use, and what we can deprecate support for. This call does not run when your app is running on your user’s devices — only when your source code is compiled. You can see exactly how & what we collect, as well as the rationale for it in our source code.

Couldn’t load “librealm-jni.so”

If your app uses other native libraries that don’t ship with support for 64-bit architectures, Android will fail to load Realm’s librealm-jni.so file on ARM64 devices. This is because Android cannot load 32-bit and 64-bit native libraries concurrently. The best solution would be to have all libraries provide the same set of supported ABIs, but sometimes that may not be doable if you are using a 3rd-party library. See VLC and Realm Library conflicts.

The workaround to this issue is to exclude Realm’s ARM64 library from the APK file by adding the following code to the app’s build.gradle. You can refer to Mixing 32- and 64-bit Dependencies in Android for more information.

android {
    //...
    packagingOptions {
        exclude "lib/arm64-v8a/librealm-jni.so"
    }
    //...
}

Also, there is a bug with Android Gradle Plugin 1.4.0 betas that leads it to improperly pack .so files included in jar files (see Realm Java issue 1421). To solve this problem, you can revert to Android Gradle Plugin 1.3.0 or use Android Gradle Plugin 1.5.0+.

We are aware of a number of 3rd party libraries, frameworks and management apps which do not have 64-bit support yet:

How to back up and restore Realm?

Realms are stored in files on the file system. By calling the getPath() you can get the full path of a Realm file. If you plan to back up or restore a Realm file this way, all instances of the Realm should be closed.

It is also possible to backup an open Realm file using realm.writeCopyTo(File).

If you want to backup a file to an external location like Google Drive. You can read this tutorial: part 1, part 2, and part 3.

Blackberry devices

Some Blackberry devices are capable of running Android apps. Unfortunately, the provided runtime environment is not complete, and we cannot guarantee compatibility. Known error messages include:

io.realm.exceptions.RealmFileException: Function not implemented in io_realm_internal_SharedRealm.cpp line 81 Kind: ACCESS_ERROR.

If you see issues with Blackberry devices, please consider to contribute a fix since both Realm Core and Realm Java are open source projects.

How to store and retrieve the encryption key used by Realm

Using Android KeyStore is probably the most secure way to store the Realm’s encryption key. Here is a recommended way to use it.

  1. Using Android’s KeyStore, generate an asymmetric RSA key, that will be stored/retrieved securely by Android. On version >= M the system requires user PIN (or finger print) to unlock the KeyStore, so even on rooted devices you have an extra layer of security.

  2. Generate a symmetric key (AES), that will be used to encrypt the Realm.

  3. Encrypt the symmetric AES key using your private RSA key.

  4. Now it’s safe to store the encrypted AES key on filesystem (in a SharedPreferences for example).

  5. When you need to use your encrypted Realm, retrieve your encrypted AES key, decrypt it using the public RSA key, then use it in the RealmConfiguration to open the encrypted Realm.

We did implement similar steps for this library if you need an end-to-end example https://github.com/realm/realm-android-user-store

Here’s an example using the fingerprint API as well.