Realm is the top choice for Android database solutions

From the inception of Android, we app developers have relied on SQLite for our local data storage needs. Whether we interact with it directly through SQL statements or utilize an Object-Relational Mapper (ORM) for abstraction, SQLite has been the bedrock.

However, despite its merits, SQLite’s relational model has its limitations. We often yearn for a database that aligns with the data structures used in our applications, minimizing the need for code conversions and intricate mappings. A database that excels in memory efficiency, especially beneficial for resource-constrained devices, would be ideal.

These aspirations are directly addressed by Realm, a database platform with a unique architecture that stands as a compelling alternative to SQLite.

This article delves into the reasons behind Realm’s growing popularity and why you should consider it. We will explore the distinct advantages Realm offers Android developers over SQLite.

While our focus is on Android, the concepts discussed here extend to other mobile platforms where Realm shines, including iOS, Xamarin, and React Native.

SQLite: Functional but Often Not Ideal

Most mobile developers are well-acquainted with SQLite. Introduced in 2000, it is arguably the most used relational database engine globally.

SQLite’s appeal lies in its benefits, such as native Android support and its adherence to standard SQL, easing the learning curve for developers familiar with relational databases. It delivers commendable performance when fully utilized (using prepared statements, bulk operations with transactions, etc.), although its scalability may not suit all needs.

However, working directly with SQL statements has its downsides.

As outlined by the official Android documentation, interacting with SQLite involves:

  1. Defining your schema using contract classes.
  2. Writing create/drop table commands as strings.
  3. Extending SQLiteOpenHelper to execute create commands and manage schema upgrades/downgrades.

These steps only pave the way for database interactions. You still need to handle the back-and-forth conversion between application objects and database values. In essence, it involves a significant amount of boilerplate code.

Maintainability poses another challenge. As your project expands and queries become more intricate, you’ll grapple with extensive raw SQL queries embedded in strings. Modifying the logic of these queries later can prove cumbersome.

Despite these drawbacks, raw SQL usage remains relevant in specific cases, such as library development where performance and size are paramount, and incorporating a third-party library is best avoided.

ORMs: Addressing SQL Challenges Partially

ORMs emerged to liberate us from the complexities of raw SQL.

Notable Android ORMs include DBFlow, greenDAO, and OrmLite.

Their primary contribution is abstracting SQLite, simplifying the mapping of database entities to Java objects.

Among other advantages, developers can work with objects, a more familiar data structure. Maintainability is enhanced as we handle high-level, strongly-typed objects, leaving the intricacies to the libraries. The need to construct queries by concatenating strings or manage database connections manually is reduced, minimizing errors.

While ORMs undoubtedly elevate Android database interactions, they aren’t without flaws. One common issue is the loading of superfluous data.

Consider a table with 15 columns. On a screen displaying a list of objects from this table, only three columns are relevant. Loading all data from each row retrieves five times the necessary data.

Some libraries allow you to specify the columns to retrieve, but this requires additional code. Even then, inefficiencies may arise if the required columns are determined only after data examination.

Furthermore, complex queries often lack adequate representation within ORM APIs, leading to inefficient queries performing unnecessary calculations.

This performance hit might necessitate resorting to raw SQL, compromising the core purpose of object-relational mapping and reintroducing the aforementioned SQLite issues.

Realm: A Compelling Alternative

Realm Mobile Database is purpose-built for mobile devices from the ground up.

Unlike ORMs that abstract SQLite, Realm is a completely independent database engine. It employs an object store model rather than a relational one, with its core comprising a self-contained C++ library. Currently, it supports Android, iOS (Objective-C and Swift), Xamarin, and React Native.

Launched in June 2014, Realm is relatively new (around two and a half years old!).

While server database technologies have witnessed a revolution since 2007, with numerous innovations, mobile database technology remained tied to SQLite and its wrappers. This stagnation was a key driver behind creating Realm from scratch. Moreover, some of Realm’s features necessitate fundamental changes in database behavior at a low level, impossible to achieve by building upon SQLite.

But is Realm truly worthwhile? Let’s examine the compelling reasons to embrace it.

Effortless Modeling

Consider these Realm models:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class Contact extends RealmObject {
    @PrimaryKey
    String id;
    protected String name;
    String email;
    @Ignore
    public int sessionId;

    //Relationships
    private Address address;
    private RealmList<Contact> friends;

    //getters & setter left out for brevity
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class Address extends RealmObject {
    @PrimaryKey
    public Long id;

    public String name;
    public String address;
    public String city;
    public String state;
    public long phone;
}

Your models inherit from RealmObject. Realm supports primitive types, their boxed counterparts (excluding char), String, Date, byte[], subclasses of RealmObject, and RealmList<? extends RealmObject> for relationships.

Field access levels (private, public, protected, etc.) are flexible. All fields are persisted by default, with annotations used for “special” fields (@PrimaryKey for primary keys, @Ignore for non-persisted fields, etc.).

This approach minimizes “annotation clutter” compared to ORMs, which often require annotations for mapping classes to tables, fields to columns, foreign keys, and so on.

Relationships

Realm offers two ways to represent relationships:

  • Embedding a model as a field within another model. In our example, the Contact class contains an Address field, defining their relationship. A contact may have an address, but the same address can be associated with other contacts, allowing for one-to-one and one-to-many relationships.

  • Using a RealmList of the referenced models. RealmLists resemble Java Lists, acting as containers for Realm objects. Our Contact model includes a RealmList of contacts representing friends. This approach facilitates one-to-many and many-to-many relationships.

This representation feels intuitive for Java developers. By including objects (or lists of objects) directly as fields, mirroring non-model classes, we bypass the need for intricate SQLite foreign key configurations.

Caveat: Realm lacks direct support for model inheritance. The current workaround involves using composition. Instead of a Dog model extending from Animal, you would include an Animal instance as a field within Dog. The debate on Composition vs. Inheritance is extensive. If you heavily rely on inheritance, this is a crucial consideration. In SQLite, this could be achieved using two tables (parent and child) linked by a foreign key. Some ORMs, like DBFlow, don’t impose this restriction.

Data Efficiency: Zero-copy Design

This is a game-changer.

Realm utilizes a zero-copy design, meaning data is never copied to memory. Query results are essentially pointers to the actual data, lazily loaded upon access.

For instance, in a model with 10 fields (SQL columns), if you query objects for a list display requiring only three fields, only those three are retrieved.

This results in exceptionally fast queries (refer to here and here for benchmarks).

This is a significant advantage over ORMs, which typically load all data from selected SQL rows upfront.

Screen loading becomes remarkably efficient without developer intervention; it’s Realm’s inherent behavior.

Moreover, reduced memory consumption is a boon for resource-constrained mobile devices.

The zero-copy approach also enables automatic object updates.

Since data isn’t copied, if another thread modifies data after your query, your results, being pointers, reflect those changes instantly. Accessing field values always returns the most up-to-date data.

Illustration: Acessing Realm data from multiple objects and threads.

To receive updates when underlying data changes after reading from Realm objects, add a listener:

1
2
3
4
5
6
7
final RealmResults<Contact> johns = realm.where(Contact.class).beginsWith("name", "John ").findAll();
johns.addChangeListener(new RealmChangeListener<RealmResults<Contact>>() {
      @Override
      public void onChange(RealmResults<Contact> results) {
          // UPDATE UI
      }
});

Beyond a Simple Wrapper

While numerous ORMs exist, they remain wrappers limited by the underlying SQLite. In contrast, Realm is a complete database engine, affording it capabilities beyond ORM reach.

One fundamental difference is Realm’s ability to store data as an object graph store.

This means objects are used throughout, from the programming language to the database, minimizing conversions compared to relational databases.

Database structures closely mirror those used by developers. This shift from relational to aggregate models is a trend even in server-side development. Realm brings these concepts to mobile development.

Realm’s architecture consists of a core with the fundamental implementation and binding libraries tailored to each supported platform.

Illustration: Realm architecture from core to various libraries.

Wrappers often necessitate abstraction layers due to their reliance on external technologies.

Realm’s binding libraries are intentionally thin, minimizing abstraction complexity and closely reflecting the Core’s design. This architectural control ensures component harmony.

For example, accessing referenced objects (foreign keys in SQL) in Realm is efficient. Its file structure relies on native links. Querying relationships doesn’t involve translating ORM abstractions or joining tables but directly accessing objects via raw links at the file system level.

This means objects point directly to other objects. Querying a relationship is as straightforward as querying an integer column, involving pointer traversal rather than expensive foreign key operations.

Strong Community & Support

Realm is actively developed, with frequent updates and new releases.

All Realm Mobile Database components are open-source. Their responsive team on issue tracker and Stack Overflow provides timely support.

Community feedback is instrumental in prioritizing issues (bugs, improvements, feature requests, etc.), giving developers a voice in the tool’s evolution.

Having used Realm since 2015, I’ve encountered various opinions online. While we’ll address its limitations shortly, it’s worth noting that many past complaints have been addressed.

For instance, the lack of custom methods on models and asynchronous calls, once deal breakers, are now supported.

This development speed and responsiveness instill confidence that crucial features won’t be long overdue.

Limitations

While impressive, Realm isn’t without drawbacks. Besides the inheritance limitation mentioned earlier:

  • Though concurrent read/write access from multiple threads is possible, Realm objects are not thread-safe. For example, you cannot pass a Realm object retrieved in AsyncTask’s doInBackground() (background thread) to onPostExecute() (main thread). Workarounds include copying the object or passing its ID and retrieving it again in onPostExecute(). Realm offers both synchronous and asynchronous read/write methods.

  • Auto-increment primary keys are not supported. You need to manage their generation manually.

  • Concurrent database access from different processes is not possible. Multi-process support is reportedly on the horizon.

Realm: The Future of Mobile Databases

SQLite remains a dependable, robust, and proven database engine, and relational databases are here to stay. Numerous capable ORMs effectively address various scenarios.

However, staying abreast of emerging trends is crucial.

Realm represents one of the most significant advancements in mobile database development in recent years.

Its unique data handling approach benefits developers by offering a compelling alternative to existing solutions while expanding possibilities and raising the bar for mobile database technology.

Have you incorporated Realm into your projects? We’d love to hear your experiences and insights!

Licensed under CC BY-NC-SA 4.0