Local Databases in Android: Choosing What’s Best for Your Project

Robert Levonyan
ProAndroidDev
Published in
6 min readMar 4, 2021

--

Caching in Android is an interesting task and there are quite a few ways you can do that. But here’s the question — Which way is the best for me? With this article I want to try to answer that question, and help you understand which of the given libraries or frameworks fits the best for your next project. I am going to compare the most popular caching mechanisms for Android that are available in the market for the moment.

Let’s see the implementation and usage of each, and compare some numbers at the end!

Image from Dribbble (https://dribbble.com/shots/9190772-Man-in-storage-room)

Let’s write a simple app that will download a list of movies from an API and store it in a local database to show it later (let’s say when your device is offline). I will use the following frameworks for caching:

  • Room
  • Realm
  • SQL Delight
  • ObjectBox

Let’s compare the implementation, usage, and speed of these 4.

NOTE! You can find a link to a sample project at the end of this Article

Integration

Let’s see what we need to do to add each framework to our project.

Room

To add Room to your project, only thing you need to dois to add its dependencies.

implementation "androidx.room:room-runtime:$roomVersion"
implementation "androidx.room:room-ktx:$roomVersion" // Kotlin only
kapt "androidx.room:room-compiler:$roomVersion" // annotation processor

Realm

To use realm, you need to add the corresponding plugin.

//project level build.gradledependencies {
...
classpath "io.realm:realm-gradle-plugin:$realmVersion"
...

}
// app level build.gradleapply plugin: 'com.android.application'
apply plugin: 'realm-android'

ObjectBox

For ObjectBox, you need to do both.

//project level build.gradledependencies {
...
classpath "io.objectbox:objectbox-gradle-plugin:$obVersion"
...

}
// app level build.gradleapply plugin: 'com.android.application'
apply plugin: 'io.objectbox'
...dependencies {
...
implementation "io.objectbox:objectbox-kotlin:$obVersion"
...
}

SqlDelight

Same as ObjectBox.

//project level build.gradledependencies {
...
classpath "com.squareup.sqldelight:gradle-plugin:$delightVersion"
...

}
// app level build.gradleapply plugin: 'com.android.application'
apply plugin: 'com.squareup.sqldelight'
...dependencies {
...
implementation "com.squareup.sqldelight:android-driver:$delightVersion"
implementation "com.squareup.sqldelight:coroutines-extensions-jvm:$delightVersion"
...
}

Implementation

In this part, let’s discuss entities and queries.

Room

@Entity
data class Movie(
@PrimaryKey
val id: Long,
val backdropPath: String,
val posterPath: String,
val title: String,
val overview: String,
@TypeConverters(DateTypeConverter::class)
val releaseDate: Date,
)

As Room is SQLite-based, it can store only primitive data types and text. All other objects need to be converted to one of the supported types (most often String) using TypeConverter API.

Here is an example of Data Access Object interface

To store, get, update or delete data in room, you need to create an interface annotated with @Dao annotaion. Also, with @Insert, @Update, @Delete annotations, you can create corresponding functions without implementing a query. To get data from your DB, you need to create a function, annotate it with @Query annotation and pass query itself as a parameter of the annotation.

Room has a support of both RxJava and Kotlin Coroutines, so you can make your functions to return RxJava Future types or make them suspending.

Note! You can set the return type of the function with SELECT query kotlinx.coroutines.flow and get instant updates each time data in DB is changed.

To access the DAOs, you need to create an AppDatabase instance.

An example of an App Database class

Realm

open class Movie : RealmObject() {
@PrimaryKey
var id: Long = 0L
var backdropPath: String = ""
var posterPath: String = ""
var title: String = ""
var overview: String = ""
var releaseDate: Date = Date()
}

In Realm, you cannot have a data class as an entity because for Realm, it is required that the entity class will have an empty constructor. To make a class your Realm entity just extend it from RealmObject. Like Room, Realm stores primitive types and Strings, but it also can store common types like Dates. If you need to store you own custom object, it should be derived from RealmObject as well.

To be able to use Realm, it should be initiated in the Application class.

Realm.init(context)

To manipulate the data in Realm DB, you need an instance of Realm.

You can get a default instance or create your own with your own configuration.

Any operation on Realm DB is done inside a Realm transaction scope. There are two types of transactions: synchronous and asynchronous.

To start a synchronous transaction, just start it by calling realm.beginTransaction(), write your code and do not forget to save changes by calling realm.commitTransaction(). If you are like me, and you think you will always forgot to close the transaction, there is another option to try. realm.executeTransaction {…}. A transaction will be started before the execution of the lambda, and it will be automatically closed after.

But we all know, that users don’t really like when the UI freezes while we are executing heavy tasks on the Main Thread. For this reason, just use realm.executeTransactionAsync {…} or realm.executeTransactionAwait {…} if you are using Coroutines.

To save data in the database, you need to create an object with realm.createObject() function, because there are no SQL tables in Realm, everything is saved as objects. And to read you saved instance, you can create a query by calling query functions on realm.where() result.

ObjectBox

@Entity
data class Movie(
@Id(assignable = true)
var id: Long,
var backdropPath: String,
var posterPath: String,
var title: String,
var overview: String,
var releaseDate: Date,
)

The structure of ObjectBox entities is basically same as in Room.

To manipulate the data, you need to have a BoxStore instance in your project.

An example of how to create a Box Store instance

It should be initiated in the Application class.

ObjectBox.init(context)

ObjectBox has built-in RxJava support, but to use it with Coroutines, you need to implement Coroutines by yourself.

SqlDelight

From the usage perspective, SqlDelight is different from other the frameworks mentioned above.

Hierarchy of directories

To start with the SqlDelight, you will need to create a new directory in your module under the main directory on the same level with java directory and name it sqldelight. After doing this, add some nested directories according to java with same names.

Once you have created directories, add a new file with .sq extension (You can install an optional plugin for Android Studio to highlight code in .sq files)

Here is an example of my movies.sq file

It contains regular SQLite queries. Because this is an SQLite-based framework like Room, you cannot store Date objects directly, so there is an API similar to TypeConverter API in Room called ColumnAdapter.

An example of Column Adapter

The rest will be generated for you: the DB object, entities, queries, etc.

An example of storing and retrieving data with SqlDelight

Final round

Well, well, well.

We have a picture of what work we’ll need to do for each of the frameworks. But is it enough?

Let’s create a table with comparison of features and also some numbers that can help you make your final decision.

A table with some key aspect comparison

One thing to mention here, Room will work almost twice faster (111/55) if not using coroutines, but use standard implementation with a Thread, but even in that case SqlDelight will be a bit faster.

*  Both write and read speeds are measured in milliseconds
** Values are not exact, each time they run with different speed, but the ratio between them is the same

Also, you need to know, that Room and SqlDelight, because of the SQLite DB will save space of your app size, but Realm and ObjectBox will store their objects inside your app. Compile times are almost the same for all, except if you have lot’s of queries, for Room and SqlDelight it will take longer to generate all classes they need.

Conclusion

I hope this article will make it a bit easier for you to choose a local db framework for your next project.

To read more about all four frameworks, visit the links below:

Room official documentation

Realm official webpage

ObjectBox official documentation for Kotlin

SqlDelight section in CashApp blog

A demo project with all four included

Happy coding!

--

--