Realm, ObjectBox or Room. Which one is for you?

Rado Yankov
Dev Labs
Published in
7 min readSep 18, 2017

--

Choices, choices, choices. When it comes to storing data, Android developers have a plethora of available libraries. Whether it's hanging on to a couple of objects, or building a massive collection, the tools are at our disposal, waiting to get used. Some of them come out of the box, like shared preferences and pure SQL, others require external dependencies. Thankfully, I’m not here to talk about writing long illegible queries, I promise. Instead, I will be comparing the big-league players: The newly announced Room Persistence Library, the age-old Realm, and the lesser known ObjectBox, which recently came out of beta. The final choice, I leave to you, however by the end it should be (more or less) clear which one comes on top. However, before we get to the clash of the titans, let’s introduce them first.

Realm 🔗

Ever since it’s conception (around 2011, originally as “TightDB”) Realm has been the go-to choice for many developers. Why, you ask? Simplicity (uses almost standard Java objects), speed (written mostly in C++) and SQL (none of it). Without going into too much detail, creating a Realm database is simple, using it — even more so. The library needs minimal setup and the official documentation does a good job of holding your hand through the process.
A model for the object to store is the first thing that’s needed:

open class Box (
@PrimaryKey var size: Long = 0,
var name: String = "",
@Ignore var tempReference: Int = 0) : RealmObject() {}

The only thing here worth noting is that if you use Kotlin, all of your variables must have default values. The annotations and the necessary inheritance of RealmObject are (hopefully) self-explanatory, so let’s move on.

Using Realm is as simple as this:

Realm.init(context)
val realm = Realm.getDefaultInstance()
val box = realm.where(Box::class.java).findFirst()
realm.executeTransaction {
//modifying an exsiting object
box.size = 20
box.name = "John"
//creating a new object
val secondBox = realm.createObject(Box::class.java)
secondBox.size = 30
}

Full example

Note: With this being a database centered guide, I will leave the multithreading to you.

Note 2: Yes, the box’s name is John.

Room Persistence Library 🔗

Enter Room! The newest and shiniest of Google’s libraries. Taking a center place in their official architecture guidelines, Room provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite. It does a pretty good job of tucking away the SQL and exposing clean, understandable Java methods to the developer. So, remember when I promised no queries? Well now we’re going to write some queries! But don’t worry, Room includes some safety features, which alert you in case of nasty mistakes.

Since, at the time of writing this article (and probably long after that), Room is the popular kid in town, I’ll keep its introduction as short as I can.

There are 3 major components in Room, all represented as annotations:

Database: You can use this component to create a database holder. The annotation defines the list of entities, and the class’s contents- the list of data access objects (DAOs) in the database. It is also the access point for the underlying connection.
The annotated class should be an abstract class that extends RoomDatabase. You can acquire an instance of it by calling Room.databaseBuilder() or Room.inMemoryDatabaseBuilder().

Entity: This component represents a class that holds a database row. For each entity, a database table is created to hold the items. You must reference the entity class through the entities array in the Database class.

DAO: This component represents a class or interface as a Data Access Object. DAOs are responsible for defining the methods that access the database. The class that is annotated with @Database must contain an abstract method that has 0 arguments and returns the class that is annotated with @Dao.

The following are 3 implementations (shamelessly copied from this excellent article) of the above-mentioned components:

@Entity(tableName = “task”)
data class Task(@ColumnInfo(name = “completed_flag”) var completed: Boolean = false,
@ColumnInfo(name = “task_desciption”) var description: String) {
@ColumnInfo(name = “id”)
@PrimaryKey(autoGenerate = true) var id: Long = 0
}
@Dao interface TaskDao {
@Query(“select * from task”)
fun getAllTasks(): List<Task>
@Query(“select * from task where id = :p0”)
fun findTaskById(id: Long): Task
@Insert(onConflict = REPLACE)
fun insertTask(task: Task)
@Delete
fun deleteTask(task: Task)
}
@Database(entities = arrayOf(Task::class), version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
abstract fun taskDao(): TaskDao
}

Creating a database and calling its methods is as simple as this:
var database = Room.databaseBuilder(context, AppDatabase::class.java,”db”).allowMainThreadQueries().build()

database.taskDao().insertTask( Task(description = “simple!”) )

ObjectBox 🔗

Being the newest of the bunch, ObjectBox brings a lot to the table. But with the bar set this high already, can this new NoSQL technology compare to the veterans in the field? Surely it must pack quite the punch, if it’s to go head-to-head with Realm and Room. And indeed, it packs not one, but a whole series of punches. Here are some of the top highlights of the newcomers:

Speed: Just like Realm, ObjectBox offers excellent performance, sometimes even surpassing its competitors (more on that later).

QueryBuilder: With ObjectBox you simply query for objects with checks at compile time.

Object Relations: Object references / relationships are a built-in type; they are native references.

No manual schema migrations: It takes care of new object versions with added, removed and renamed properties.

Et cetera, et cetera.

So how does it look like in practice?

The must-have model should look familiar by now:

@Entity
data class Note (
@Id var id: Long = 0,
val text: String
)

ObjectBox works with objects called Boxes (go figure) to store and work with data. Only 2 lines divide you from working with the database:

The “Box Store” object which, preferably, should reside in your Application class:

MyObjectBox.builder().androidContext(App.this).build()

And individual “Boxes” for each of the models that you have in your database. These boxes will serve as an interaction point between you and the database.

var notesBox = boxStore.boxFor(Note::class.java)

An important detail is that these Box types are automatically generated, meaning less things to worry about!

Once you have this down, you are ready to go, and these are some of the available methods for you to use:

notesBox.put(note)
notesBox.remove(note)
notesBox.count()

For a complete list of methods available in the Box class, check its JavaDoc. A thing to note here is the so-called DaoCompat compatibility layer which allows for greenDAO like API for ObjectBox.

The Comparison

So far, all libraries have done more or less the same thing, some with, some without the use of SQL. However, it’s their differences that interest us. In the images that follow, I’ve tested the performance of each of the 3 approaches, using this open-source benchmarking app.

Performance for 100k/10k elements measured in ms

Some pretty interesting results, wouldn’t you say? As clearly seen from the tests, most of the times, ObjectBox crushes all competition. And, of course, the gap gets even wider the more elements are tested! Not bad, for a newcomer. Not bad at all.

Queries also seem to be one of ObjectBox’s strong sides. Tests are with string and index, and the results speak for themselves.

How about apk size? How much will each one of these libraries slow down our project? Well, we can use the newly released apk analyzer to see exactly how heavy each one is.

ObjectBox and Realm take up to 1–1.5MB and 3–4MB respectively (the size depends on the phone architecture), while Room, being an SQL wrapper, takes up only about 50KB. But being the obedient Android developers that we are, we must also follow that pesky method count limit. In that regard Room, again, seems to lead the race, with its modest 300 methods. Following is ObjectBox with 1300 and Realm with 2000 methods.

Feature-wise, each of the contenders offer something extra. Room provides everything that you can do with SQLite plus some more on the side. There is the migration mechanism, and it is of course, completely testable. Conversely, ObjectBox doesn’t even need one, because it handles most of the migrations by itself (although for some changes, it does require additional info, to make things unambiguous). Realm packs the most impressive arsenal of features including custom configurations, encryption and many more (one of the reasons for its size).

Conclusion

We can see that whichever path we choose to follow, each has its ups and downs. If you are looking for speed and efficiency, ObjectBox is a clear choice. However, if you’re limited by app size, getting close to that 64k method limit and, of course, if you’re willing to deal with SQL, Room might be a solution for you. Realm, on the other hand, might not be the quickest, nor the smallest of the lot, but they offer the most stable, bug free and sane solution with more than 7 years of debugging and improvements behind their back.

I’ll leave it to you but remember, an app is only as good as what you choose for it (and how well you code it, but that’s another topic).

--

--